解析一组表达式时发生冲突

时间:2013-07-23 21:54:57

标签: parsing lexical-analysis ocamllex ocamlyacc menhir

我想解析一组表达式:R[3]CR[2]CR[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,其中32index`R[3]CR[2]Ce_cellR[3]C-R[2]Ce_expression

有人可以帮忙吗?

2 个答案:

答案 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之后,前瞻是-,我们需要:

  1. R [ index ] C缩减为e_cell

  2. 转移-,提供R [ index ] C -

  3. 如果没有更多的前瞻,我们无法分辨:以下令牌必须是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语句出现问题,但是你可以使内部逻辑更复杂以解决这个问题。