我正在编写一个类似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 ';'
答案 0 :(得分:2)
问题是function_def
只能在function_list
之后发生,这意味着解析器需要先减少空function_list
(使用生产function_list → ε
)它可以识别function_def
。此外,它需要通过仅查看空生产之后的令牌来做出该决定。由于该令牌(type_name
)可以启动var_decl
或function_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_decl
和function_def
在第三个令牌之前看起来一样无关紧要。
问题实际上是很难找出什么都没有。