转换/减少C变异语法的冲突

时间:2015-02-23 01:44:00

标签: parsing bison yacc

我正在编写一个类似C语法的解析器,但是我遇到了一个转移/减少冲突的问题:

基本上,语法接受一个可选的全局变量声明列表,后跟函数。

我有以下规则:

program: global_list function_list;

type_name : TKINT /* int */
          | TKFLOAT /* float */
          | TKCHAR /* char */

global_list : global_list var_decl ';'
            |
            ;

var_decl : type_name NAME;

function_list : function_list function_def
              |
              ;

function_def : type_name NAME '(' param_list ')' '{' func_body '}' ;

我知道我遇到了问题,因为语法无法决定下一个 type_name NAME 是否属于global_list或function_list,默认情况下它是期望global_list

例如:

int var1;

int foo(){}

error: unexpcted '(', expecting ';'

1 个答案:

答案 0 :(得分:2)

问题是function_def只能在function_list之后发生,这意味着解析器需要先减少空function_list(使用生产function_list → ε)它可以识别function_def。此外,它需要通过仅查看空生产之后的令牌来做出该决定。由于该令牌(type_name)可以启动var_declfunction_def,因此解析器无法决定。

即使决定另外一个令牌也无济于事;直到第三个标记才能做出正确的决定。所以你的语法不明确,但它是LR(3)。

可能为不同类型的空列表的序列总是会产生此问题。相比之下,非空列表序列没有,因此解决问题的第一种方法是消除ε-产生。

首先,我们扩展顶级定义,以明确两个列表都是可选的:

program: global_list function_list;
       | global_list
       | function_list
       |
       ;

然后我们将两个列表类型都设为非空:

global_list
       : var_decl
       | global_list var_decl
       ;

function_list
       : function_def
       | function_list function_def
       ;

其余的语法没有改变。

type_name : TKINT /* int */
          | TKFLOAT /* float */
          | TKCHAR /* char */

var_decl : type_name NAME;

function_def : type_name NAME '(' param_list ')' '{' func_body '}' ;

值得注意的是,如果声明可以穿插,问题就永远不会出现。是否真的有必要在任何函数之前定义所有全局变量?如果没有,您可以使用单个列表类型,这也是无冲突的:

program: decl_list ;

decl_list:
         | decl_list var_decl;
         | decl_list function_def
         ;

这两种解决方案都有效,因为自下而上的解析器可以等到生产结束时减少,以便确定哪个是正确的缩减; var_declfunction_def在第三个令牌之前看起来一样无关紧要。

问题实际上是很难找出什么都没有。