【问题标题】:Bison Reduce/Reduce Conflict with Casting and Expression ParenthesesBison 减少/减少与强制转换和表达式括号的冲突
【发布时间】:2015-02-11 10:22:52
【问题描述】:

我正在用 bison 构建一个语法,并且我已将我上次的 reduce/reduce 错误缩小到以下测试用例:

%{
#include <stdio.h>
#include <string.h>

extern yydebug;

void yyerror(const char *str)
{
  fprintf(stderr, "Error: %s\n", str);
}

main()
{
  yydebug = 1;
  yyparse();
}
%}

%right '='
%precedence CAST
%left '('

%token AUTO BOOL BYTE DOUBLE FLOAT INT LONG SHORT SIGNED STRING UNSIGNED VOID

%token IDENTIFIER

%start file

%debug

%%

file
  : %empty
  | statement file
  ;

statement
  : expression ';'
  ;

expression
  : expression '=' expression
  | '(' type ')' expression %prec CAST
  | '(' expression ')'
  | IDENTIFIER
  ;

type
  : VOID
  | AUTO
  | BOOL
  | BYTE
  | SHORT
  | INT
  | LONG
  | FLOAT
  | DOUBLE
  | SIGNED
  | UNSIGNED
  | STRING
  | IDENTIFIER
  ;

问题可能是当它在表达式中看到(IDENTIFIER) 时,它无法区分类型和表达式。

输出:

fail.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
fail.y:64.5-14: warning: rule useless in parser due to conflicts [-Wother]
   | IDENTIFIER
     ^^^^^^^^^^

我能做些什么来解决这个冲突?

【问题讨论】:

    标签: parsing compiler-construction bison yacc


    【解决方案1】:

    如果语法仅限于 OP 中显示的产生式,则消除冲突相对容易,因为语法是明确的。唯一的问题是它是 LR(2) 而不是 LR(1)。

    OP中的分析是完全正确的。当解析器看到时,例如:

    ( identifier1 · )
    

    (其中·标记了当前点,所以lookahead token是),无法知道那是不是的前缀

    ( identifier1 · ) ;
    ( identifier1 · ) = ...
    ( identifier1 · ) identifier2
    ( identifier1 · ) ( ...
    

    在前两种情况下,identifier1 必须简化为 expression,以便随后可以将 ( expression ) 简化为 expression,而在后两种情况下, identifier1 必须简化为 type,以便 ( type ) expression 随后可以简化为 expression。如果解析器可以在更远的将来看到一个标记,则可以做出决定。

    因为对于任何 LR(k) 文法,都有一个 LR(1) 文法可以识别相同的语言,显然有一个解决方案;一般的方法是推迟减少,直到一个标记的前瞻足以区分。一种方法是:

    cast_or_expr   : '(' IDENTIFIER ')'
                   ;
    cast           : cast_or_expr
                   | '(' type ')'
                   ;
    expr_except_id : cast_or_expr
                   | cast expression %prec CAST
                   | '(' expr_except_id ')'
                   | expression '=' expression
                   ;
    expression     : IDENTIFIER
                   | expr_except_id
                   ;
    

    (语法的其余部分相同,只是从 type 的产生式中删除了 IDENTIFIER。)

    这适用于没有符号既可以是前缀又可以是中缀运算符(如-)并且没有运算符可以被省略(实际上,如在函数调用中)的语法。特别是,它不适用于 C,因为它会留下歧义:

    ( t ) * a   // product or cast?
    ( t ) ( 3 ) // function-call or cast?
    

    这些是语法中真正的歧义,只能通过知道 t 是类型名还是变量/函数名来解决。

    C 解析器的“通常”解决方案是通过在扫描器和解析器之间共享符号表来解决歧义;由于typedef 类型别名声明必须出现在第一次使用符号作为适用范围内的类型名之前,因此可以在扫描令牌之前知道令牌是否已用typedef 声明。更准确地说,如果没有看到 typedef,则可以假定该符号不是类型,尽管它可能完全未声明。

    通过使用 GLR 语法和语义谓词,可以将逻辑限制在解析器中。有些人觉得这更优雅。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多