Bison 解决二次前瞻中的野牛冲突

Bison 解决二次前瞻中的野牛冲突,bison,Bison,我正在为一个项目编写解析器,但遇到了一个问题。下面是一个独立的问题示例: %error-verbose %token ID %token VAR %token END_VAR %token CONSTANT %token AT %token VALUE %% unit: regular_var_decl | direct_var_decl; regular_var_decl: VAR constant_opt ID ':' VALUE ';' END_VAR; constant_

我正在为一个项目编写解析器,但遇到了一个问题。下面是一个独立的问题示例:

%error-verbose

%token ID
%token VAR
%token END_VAR
%token CONSTANT
%token AT
%token VALUE
%%

unit: regular_var_decl
    | direct_var_decl;

regular_var_decl: VAR constant_opt ID ':' VALUE ';' END_VAR;

constant_opt: /* empty */ | CONSTANT;

direct_var_decl: VAR ID AT VALUE ':' VALUE ';' END_VAR;

%%
#include <stdlib.h>
#include <stdio.h>

yylex() {
  static int i = 0;

  static int tokens[] = {
    VAR,
      ID, ':', VALUE, ';',
    END_VAR,
    0
  };

  return tokens[i++];
};

yyerror(str) char *str; {
  printf("fail: %s\n", str);
};

main() {
  yyparse();
  return 0;
};
%错误详细
%令牌ID
%令牌变量
%令牌端变量
%标记常数
%代币
%代币价值
%%
单位:常规变量数据
|直接变量;
常规变量decl:var常量变量ID':'VALUE';'结束变量;
常数_opt:/*空*/|常数;
直接变量:变量ID在值“:”值“;”结束变量;
%%
#包括
#包括
yylex(){
静态int i=0;
静态整数令牌[]={
VAR,
ID“:”,值“;”,
(完),
0
};
返回令牌[i++];
};
yyerror(str)字符*str;{
printf(“失败:%s\n”,str);
};
main(){
yyparse();
返回0;
};
可以构建它
bison test.y&&cc test.tab.c&&a.out

它警告我由于冲突,
constant\u opt
无效


这种歧义可以通过使用LALR(2)来解决,因为在
ID
之后,它可以在
处找到“:”或
。。。如何解决bison的这个问题?

一个简单的解决方案就是不要缩写可选常量:

regular_var_decl:  VAR ID ':' VALUE ';' END_VAR;
constant_var_decl: VAR CONSTANT ID ':' VALUE ';' END_VAR;
direct_var_decl:   VAR ID AT VALUE ':' VALUE ';' END_VAR;
这就允许在知道足够的信息之前推迟做出缩减决策。(如果有用的话,您可以将
':“VALUE”;“END_VAR
因子化为非终端。)

另一种可能是让语法保持原样,让bison生成一个GLR解析器(
%GLR parser
)。GLR解析器将有效地保留两个(或多个)并行解析,直到可以解决歧义,这肯定会解决
常量_opt
问题。(请注意,移位/减少冲突仍然由bison报告;在运行时发现一个歧义句子之前,它无法判断语言是否真的是歧义的。)大多数情况下,不需要对语法进行额外的更改,但它确实会使解析速度减慢一点

最后一种可能(此处可能不太有用)是接受语言的超集,然后在操作中发出错误消息:

var_decl: VAR const_opt ID at_value_opt ':' VALUE ';' END_VAR {
   if (/* pseudocode */ $2 && $4) { /* flag a syntax error */ }
}

这取决于两个
opt
终端返回的语义值,可以通过某种方式查询该值是否为空。

另一个解决方案是将其进一步分解:

var_decl: VAR constant_opt ID direct_opt ':' VALUE ';' END_VAR;

constant_opt: /* empty */ | CONSTANT;

direct_opt: /* empty */ | AT VALUE;

然后,在对
var_decl
的操作中,您决定它是常规的、常量的还是直接的,或者如果它同时具有
常量
AT值
,则发出错误。这样做的好处是,对于后一种情况,您可以给出自定义的、明确的错误消息,而不仅仅是一般的“语法错误”消息。

虽然这可能会起作用,但我会给我带来很多麻烦。我已经从一个相当大的EBNF规范中编写了语法,其中有很多
blabla_opt
。在过去的五天里,我一直在“翻译”它,我相信用钉子再做一次比用这种方式解决这个问题更容易(@PauloTorrens:OK,添加了GLR解析器选项。请确保您有最新的bison版本。我将采用GLR方法;看起来这是最能减少我麻烦的方法…尽管我还有大约1200行需要修复。谢谢。:D(“reduce”,明白了吗?)@PauloTorrens:GLR解析器的主要烦恼是bison继续报告所有冲突,而你不知道哪些冲突很重要。如果发现不明确的输入,它将产生运行时错误。祝你好运。遗憾的是,正如我前面所说,我得到了语法(大约1200行)根据EBNF标准。这种方法需要在我还不完全熟悉的语言上做太多的工作。我同意。这是我回答中的第三个建议:)