2009-11-15 37 views
17

當我嘗試在以下文件上使用yacc時,出現錯誤衝突:1 shift/reduce 如何找到並解決衝突?如何在這個yacc文件中找到shift/reduce衝突?

/* C-Minus BNF Grammar */ 

%token ELSE 
%token IF 
%token INT 
%token RETURN 
%token VOID 
%token WHILE 

%token ID 
%token NUM 

%token LTE 
%token GTE 
%token EQUAL 
%token NOTEQUAL 
%% 

program : declaration_list ; 

declaration_list : declaration_list declaration | declaration ; 

declaration : var_declaration | fun_declaration ; 

var_declaration : type_specifier ID ';' 
       | type_specifier ID '[' NUM ']' ';' ; 

type_specifier : INT | VOID ; 

fun_declaration : type_specifier ID '(' params ')' compound_stmt ; 

params : param_list | VOID ; 

param_list : param_list ',' param 
      | param ; 

param : type_specifier ID | type_specifier ID '[' ']' ; 

compound_stmt : '{' local_declarations statement_list '}' ; 

local_declarations : local_declarations var_declaration 
        | /* empty */ ; 

statement_list : statement_list statement 
       | /* empty */ ; 

statement : expression_stmt 
      | compound_stmt 
      | selection_stmt 
      | iteration_stmt 
      | return_stmt ; 

expression_stmt : expression ';' 
       | ';' ; 

selection_stmt : IF '(' expression ')' statement 
       | IF '(' expression ')' statement ELSE statement ; 

iteration_stmt : WHILE '(' expression ')' statement ; 

return_stmt : RETURN ';' | RETURN expression ';' ; 

expression : var '=' expression | simple_expression ; 

var : ID | ID '[' expression ']' ; 

simple_expression : additive_expression relop additive_expression 
        | additive_expression ; 

relop : LTE | '<' | '>' | GTE | EQUAL | NOTEQUAL ; 

additive_expression : additive_expression addop term | term ; 

addop : '+' | '-' ; 

term : term mulop factor | factor ; 

mulop : '*' | '/' ; 

factor : '(' expression ')' | var | call | NUM ; 

call : ID '(' args ')' ; 

args : arg_list | /* empty */ ; 

arg_list : arg_list ',' expression | expression ; 

回答

18

正如mientefuego指出的那樣,語法有一個經典的「懸而未決」的問題。 您可以通過將優先級分配給導致衝突的規則來解決問題。

規則引起的衝突是:通過使ELSE和LOWER_THAN_ELSE(僞令牌)的非關聯

selection_stmt : IF '(' expression ')' statement 
       | IF '(' expression ')' statement ELSE statement ; 

先啓動:

%nonassoc LOWER_THAN_ELSE 
%nonassoc ELSE 

這讓都更優於LOWER_THAN_ELSE只是因爲LOWER_THAN_ELSE首先被宣佈。

然後,在衝突的規則必須將一個優先級分配給任一移位或降低動作:

selection_stmt : IF '(' expression ')' statement %prec LOWER_THAN_ELSE ; 
       | IF '(' expression ')' statement ELSE statement ; 

這裏,更高的優先級給予移動。我已摻入上述校正和下面列出的完整的語法:

/* C-Minus BNF Grammar */ 

%token ELSE 
%token IF 
%token INT 
%token RETURN 
%token VOID 
%token WHILE 

%token ID 
%token NUM 

%token LTE 
%token GTE 
%token EQUAL 
%token NOTEQUAL 

%nonassoc LOWER_THAN_ELSE 
%nonassoc ELSE 
%% 

program : declaration_list ; 

declaration_list : declaration_list declaration | declaration ; 

declaration : var_declaration | fun_declaration ; 

var_declaration : type_specifier ID ';' 
       | type_specifier ID '[' NUM ']' ';' ; 

type_specifier : INT | VOID ; 

fun_declaration : type_specifier ID '(' params ')' compound_stmt ; 

params : param_list | VOID ; 

param_list : param_list ',' param 
      | param ; 

param : type_specifier ID | type_specifier ID '[' ']' ; 

compound_stmt : '{' local_declarations statement_list '}' ; 

local_declarations : local_declarations var_declaration 
        | /* empty */ ; 

statement_list : statement_list statement 
       | /* empty */ ; 

statement : expression_stmt 
      | compound_stmt 
      | selection_stmt 
      | iteration_stmt 
      | return_stmt ; 

expression_stmt : expression ';' 
       | ';' ; 

selection_stmt : IF '(' expression ')' statement %prec LOWER_THAN_ELSE ; 
       | IF '(' expression ')' statement ELSE statement ; 

iteration_stmt : WHILE '(' expression ')' statement ; 

return_stmt : RETURN ';' | RETURN expression ';' ; 

expression : var '=' expression | simple_expression ; 

var : ID | ID '[' expression ']' ; 

simple_expression : additive_expression relop additive_expression 
        | additive_expression ; 

relop : LTE | '<' | '>' | GTE | EQUAL | NOTEQUAL ; 

additive_expression : additive_expression addop term | term ; 

addop : '+' | '-' ; 

term : term mulop factor | factor ; 

mulop : '*' | '/' ; 

factor : '(' expression ')' | var | call | NUM ; 

call : ID '(' args ')' ; 

args : arg_list | /* empty */ ; 

arg_list : arg_list ',' expression | expression ; 
+2

爲什麼我應該在第一個位置創建'ELSE和LOWER_THAN_ELSE(僞令牌)非關聯'? – 2011-08-02 01:45:16

+0

哦。我忽略了它。由於ELSE和LOWER_THAN_ELSE沒有相同的優先順序,因此似乎沒有必要。 – ardsrk 2011-08-02 15:38:46

+0

%nonassoc是必要的。只使用%令牌不會告訴野牛關於優先權的任何事情。 – user764754 2011-11-19 20:38:52

0

首先,獲得state machine output from yacc。可以移位或減少的狀態代表移位/減少衝突。找到一個,然後通過重寫語法來解決衝突。

6

也許你應該試試yacc -v <filename>,它會生成細節的輸出。

我在這裏測試過,你的語法描述在經典的「dangling else」問題中失敗。

看看this Wikipedia article

+0

+1爲懸掛其他文章的鏈接! – 2011-12-13 12:47:34

0

This本文給出的替代解決方案的一個張貼由ardsrk。

+1

正確的鏈接是http://marvin.cs.uidaho.edu/Teaching/CS445/danglingElse.html – 2014-02-08 18:29:53

+0

謝謝,我糾正了正確的鏈接! – Tomato 2014-02-10 06:41:51

4

Ahem,這個問題的正確答案通常是:什麼都不做

移位/減少衝突是預期與歧義文法。他們不是錯誤,他們是衝突

衝突將通過寧願轉移減少來解決,這恰好解決了典型的懸擺問題。

野牛甚至有一個%預計ň語句,這樣,當有確切ň衝突,你沒有得到一個S/R衝突警告。

+0

%預計是一個最可怕的發明。這就像告訴你的QC部門「我們期待5個錯誤,所以如果錯誤數量恰好是5個,就發貨。」沒有區分錯誤的錯誤信息和格式化硬盤驅動器的錯誤。 :-) – 2014-01-10 17:54:57

+0

但是......但是...... s/r衝突不是*錯誤*確實,標記特定的衝突會更可取,但衝突並不完全在規則中,因此它們處於生成狀態機。所以我們用'%expect'來克服它.'歡迎來到現實世界。 – DigitalRoss 2014-01-11 17:59:32