通过第二次前瞻解决野牛冲突

Solving bison conflict over 2nd lookahead

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

%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;
};

有人可以建造它 bison test.y && cc test.tab.c && ./a.out

它警告我 constant_opt 由于冲突而无用。

这个歧义可以通过使用 LALR(2) 来解决,因为在 ID 之后它可以找到 ':' 或 AT...我如何在 bison 上解决这个问题?

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

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 解析器将有效地保留两个(或更多)并行解析,直到可以解决歧义,这肯定会解决 constant_opt 问题。 (请注意,bison 仍会报告 shift/reduce 冲突;在运行时发现歧义句子之前,它无法判断该语言是否实际上是歧义的。)大多数时候,不需要对语法进行额外的更改, 但它确实会稍微减慢解析速度。

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

var_decl: VAR const_opt ID at_value_opt ':' VALUE ';' END_VAR {
   if (/* pseudocode */  && ) { /* 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 的操作中,您决定它是规则的、常量的还是直接的,或者如果它同时具有 CONSTANTAT VALUE,则发出错误。这样做的好处是您可以为后一种情况提供自定义的、明确的错误消息,而不仅仅是一般的 "syntax error" 消息。