Parsing Bison Reduce/Reduce与强制转换和表达式括号的冲突

Parsing Bison Reduce/Reduce与强制转换和表达式括号的冲突,parsing,compiler-construction,bison,yacc,Parsing,Compiler Construction,Bison,Yacc,我正在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 '='

我正在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
  ;

如何解决此冲突?

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

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

( identifier1 · )
(其中,
·
标记当前点,因此前瞻标记为)),不可能知道这是否是的前缀

( identifier1 · ) ;
( identifier1 · ) = ...
( identifier1 · ) identifier2
( identifier1 · ) ( ...
在前两种情况下,
标识符1
必须减少到
表达式
,以便
(表达式)
随后可以减少到
表达式
,而在后两种情况下,
标识符1
必须缩减为
类型
,以便
(类型)表达式
随后可以缩减为
表达式
。如果解析器能够看到一个令牌,那么就可以做出决定

因为对于任何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
               ;
(除了从
类型
的产品中删除
标识符
之外,其余语法相同)

对于没有符号可以同时作为前缀和中缀运算符(如
-
)的语法,以及没有运算符可以省略的语法(有效地,如在函数调用中)。特别是,它对C不起作用,因为它会留下歧义:

( t ) * a   // product or cast?
( t ) ( 3 ) // function-call or cast?
这些是语法中真正的歧义,只有知道
t
是类型名还是变量/函数名才能解决

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

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

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