![](/img/trans.png)
[英]Why do I get so many 'useless rule/token' warnings in this bison grammar?
[英]Bison nonterminal useless in grammar, rule useless in parser
我正在嘗試使用 flex-bison 來“從頭開始”制作一個編譯器。 我試圖在網上尋求幫助,但我挖掘出來的東西並不多,我設法找到了一本書:約翰萊文的 flex & bison
它非常有用,但我不知道該怎么做。
這是我的彈性代碼:
%option noyywrap
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parser.tab.h"
extern FILE *yyin;
extern FILE *yyout;
int line_no = 1;
//the function of lexer analysis. Return the token
int yylex();
//error function
void yyerror();
//print statement function
void print_return(char *token);
%}
%x ML_COMMENT
alphabet [a-zA-Z]
digit [0-9]
alphanumeric {alphabet}|{digit}
print [ -~]
underscore _
identifier ({alphabet}|{underscore})+({alphanumeric}|{underscore})*
integer "0"|[0-9]{digit}*
float_number "0"|{digit}*"."{digit}+
char \'{print}\'
%%
"PROGRAM" { print_return("PROGRAM"); return PROGRAM}
"%".* { print_return("COMMENT"); return COMMENT; }
"BREAK" { print_return("BREAK"); return BREAK; }
"VARS" { print_return("VARS"); return VARS; }
"STARTMAIN" { print_return("STARTMAIN"); return STARTMAIN; }
"ENDMAIN" { print_return("ENDMAIN"); return ENDMAIN;}
"IF" { print_return("IF"); return IF; }
"THEN" { print_return("THEN"); return THEN;}
"ELSEIF" { print_return("ELSEIF"); return ELSEIF; }
"ELSE" { print_return("ELSE"); return ELSE; }
"ENDIF" { print_return("ENDIF"); return ENDIF; }
"FOR" { print_return("FOR"); return FOR; }
"TO" { print_return("TO"); return TO; }
"STEP" { print_return("STEP"); return STEP; }
"ENDFOR" { print_return("ENDFOR"); return ENDFOR; }
"SWITCH" { print_return("SWITCH"); return SWITCH; }
"CASE" { print_return("CASE"); return CASE; }
"ENDSWITCH" { print_return("ENDSWITCH"); return ENDSWITCH; }
"RETURN" { print_return("RETURN"); RETURN; }
"FUNCTION" { print_return("FUN"); return FUN; }
"ENDFUNCTION" { print_return("ENDFUNCTION"); return ENDFUNCTION; }
"PRINT" { print_return("PRINT"); return PRINT; }
"WHILE" { print_return("WHILE"); return WHILE;}
"ENDWHILE" { print_return("ENDWHILE"); return ENDWHILE;}
";" { print_return("QM"); return QM; }
"\n" { line_no++; print_return("NEWLINE"); return NEWLINE; }
"\t" { print_return("INDENT"); return INDENT; }
"+=" { print_return("ADD_ASSIGN"); return ADD_ASSIGN; }
"-=" { print_return("SUB_ASSIGN"); return SUB_ASSIGN; }
"/=" { print_return("DIV_ASSIGN"); return DIV_ASSIGN; }
"%=" { print_return("MOD_ASSIGN"); return MOD_ASSIGN; }
"--" { print_return("DEC_OP"); return DEC_OP; }
"++" { print_return("INC_OP"); return INC_OP; }
"AND" { print_return("AND_OP"); return AND_OP; }
"OR" { print_return("OR_OP"); return OR_OP; }
"==" { print_return("EQ_OP"); return EQ_OP; }
">=" { print_return("GE_OP"); return GE_OP; }
"<=" { print_return("LE_OP"); return LE_OP; }
"!=" { print_return("NE_OP"); return NE_OP; }
"{" { print_return("L_BRACE"); return L_BRACE; }
"}" { print_return("R_BRACE"); return R_BRACE; }
"," { print_return("COMMA"); return COMMA; }
"=" { print_return("ASSIGN"); return ASSIGN; }
"(" { print_return("L_PAR"); return L_PAR; }
")" { print_return("R_PAR"); return R_PAR;}
"[" { print_return("L_BRACK"); return L_BRACK; }
"]" { print_return("R_BRACK"); return R_BRACK;}
"." { print_return("DOT"); return DOT; }
"_" { print_return("UNDERSCORE"); return UNDERSCORE; }
"-" { print_return("MINUS"); return MINUS; }
"+" { print_return("PLUS"); return PLUS; }
"*" { print_return("MUL"); return MUL; }
":" { print_return("COLON"); return COLON; }
"/" { print_return("DIV"); return DIV; }
"<" { print_return("LT"); return LT; }
">" { print_return("GT"); return GT; }
[ ] ;
. { yyerror("Unkown character"); }
{identifier} { print_return("ID"); strcpy(yylval.name, yytext); return IDENTIFIER; }
{integer} { yylval.integer_val = atoi(yytext); print_return("INTEGER"); return INTEGER; }
{float_number} { print_return("FLOAT"); return FLOAT; }
{char} { print_return("CHAR"); return CHAR; }
%%
/*---------------------------------------------------------------------------------------------------------------------*/
void print_return(char *token)
{
printf("Token: %s\t\t Line: %d\t\t Text: %s\n", token, line_no, yytext);
}
這是我的野牛文件:
%{
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "print_console.c"
//pointer to input file of lexer
extern FILE *yyin;
//pointer to output file of lexer
extern FILE *yyout;
//line counter
extern int line_no;
//reads the input stream generates tokens
extern int yylex();
//temporary token save
extern char* yytext;
//Function Initilize
int yylex();
void yyerror(char *message);
%}
//struct for print_console
%union
{
char name[500];
int integer_val;
}
/* --------------------------------------- TOKENS ---------------------------------------*/
//starting symbol
%start PROGRAM
%token COMMENT
%token BREAK
%token VARS
%token QM
%token STARTMAIN
%token ENDMAIN
%token IF
%token THEN
%token ELSEIF
%token ELSE
%token ENDIF
%token FOR
%token TO
%token STEP
%token ENDFOR
%token SWITCH
%token CASE
%token ENDSWITCH
%token RETURN
%token FUNCTION
%token ENDFUNCTION
%token PRINT
%token WHILE
%token ENDWHILE
%token NEWLINE
%token INDENT
%token ADD_ASSIGN
%token SUB_ASSIGN
%token DIV_ASSIGN
%token MOD_ASSIGN
%token DEC_OP
%token INC_OP
%token AND_OP
%token OR_OP
%token EQ_OP
%token GE_OP
%token LE_OP
%token NE_OP
%token L_BRACE
%token R_BRACE
%token COMMA
%token COLON
%token ASSIGN
%token L_PAR
%token R_PAR
%token L_BRACK
%token R_BRACK
%token DOT
%token UNDERSCORE
%token MINUS
%token PLUS
%token MUL
%token DIV
%token LT
%token GT
%token FLOAT
%token CHAR
%token <name> IDENTIFIER
%token <integer_val> INTEGER
//type for access to $$
%type <integer_val> line int_op int_data
%type <name> calc_assignment
%%
/* --------------------------------------- BNF GRAMMAR ---------------------------------------*/
program: program line;
line: if_stmt {;}
| elseif_stmt {;}
| else_stmt {;}
| for_statement {;}
| function NEWLINE INDENT {;}
| function NEWLINE indent2 {;}
| function NEWLINE {;}
| function_call {;}
| comments NEWLINE {;}
| action {;}
| print NEWLINE {;}
| switch NEWLINE case NEWLINE {;}
| dictionaries NEWLINE {;}
| calc_assignment NEWLINE {;}
| NEWLINE {;} ;
/*--------- BREAK -------------*/
break:BREAK QM NEWLINE ;
/*--------- ACTION & indents -------------*/
indent2: INDENT INDENT;
indent3: INDENT INDENT INDENT;
indent4: INDENT INDENT INDENT INDENT;
indent5: INDENT INDENT INDENT INDENT INDENT;
action: INDENT line
| indent2 line
| indent3 line
| indent4 line
| indent5 line ;
/*--------- DATA TYPES -------------*/
data_type: CHAR
| INTEGER
| IDENTIFIER;
/*--------- FUNCTIONS --------------*/
function: FUNCTION IDENTIFIER L_PAR optional_parameters R_PAR ;
end_function: ENDFUNCTION NEWLINE;
function_call: IDENTIFIER L_PAR optional_parameters R_PAR
| IDENTIFIER L_PAR data_type R_PAR
| IDENTIFIER L_PAR data_type COMMA data_type R_PAR
| IDENTIFIER L_PAR data_type COMMA data_type COMMA data_type R_PAR;
/*------------ INSPECTORS -------------*/
inspector:IDENTIFIER operators IDENTIFIER
|IDENTIFIER operators INTEGER
|INTEGER operators IDENTIFIER
|INTEGER operators INTEGER ;
inspector_gen: inspector | inspector AND_OR_operators;
/*----------- IF & FOR STATEMENTS -------------*/
if_stmt:IF L_PAR inspector_gen R_PAR THEN NEWLINE action ;
elseif_stmt: ELSEIF L_PAR inspector_gen R_PAR NEWLINE action ;
else_stmt: ELSE NEWLINE action ;
end_if_stmt:ENDIF NEWLINE ;
for_statement: FOR IDENTIFIER COLON ASSIGN INTEGER TO INTEGER STEP INTEGER NEWLINE action;
end_for_statement: ENDFOR NEWLINE;
/*---------- SWITCH / CASE STATEMENT -----------------*/
switch: SWITCH L_PAR LT IDENTIFIER GT R_PAR NEWLINE action;
case: CASE L_PAR LT INTEGER GT R_PAR NEWLINE action;
end_switch: ENDSWITCH NEWLINE;
/*-------------- WHILE ---------------*/
while: WHILE L_PAR inspector_gen R_PAR NEWLINE action ;
end_wile: ENDWHILE NEWLINE;
/*-------------- OPERATORS ---------------*/
operators:EQ_OP
| GE_OP
| LE_OP
| NE_OP
| DEC_OP
| INC_OP
| LT
| GT;
AND_OR_operators:AND_OP
|OR_OP;
optional_parameters: IDENTIFIER
| optional_parameters COMMA IDENTIFIER ;
/*-------------- COMMENTS ---------------*/
comments: COMMENT;
/*-------------- PRINT ---------------*/
print: PRINT L_PAR data_type R_PAR QM;
/*-------------- MAIN ---------------*/
start_main: STARTMAIN NEWLINE action;
end_main: ENDMAIN NEWLINE ;
/* --- DICTIONARIES --- */
dictionaries: IDENTIFIER ASSIGN L_BRACE dictionary_data R_BRACE
| IDENTIFIER ASSIGN IDENTIFIER L_PAR L_BRACK L_PAR dictionary_data R_PAR R_BRACK R_PAR
IDENTIFIER ASSIGN IDENTIFIER L_PAR dictionary_data optional_parameters dictionary_data R_PAR ;
dictionary_data: data_type COLON data_type
|data_type COLON data_type COMMA dictionary_data
| data_type COMMA data_type optional_parameters
| IDENTIFIER ASSIGN data_type | /* empty */ ;
/* --- CALCULATE --- */
calc_assignment: IDENTIFIER ASSIGN int_op { Change($1, $3); };
int_op: int_data { $$ = $1; }
| int_op PLUS int_data { $$ = $1 + $3; }
| int_op MINUS int_data { $$ = $1 - $3; }
| int_op MUL int_data { $$ = $1 * $3; }
| int_op DIV int_data { $$ = $1 / $3; } ;
int_data: INTEGER { $$ = $1; }
| IDENTIFIER { $$ = Search($1) -> integer_val; };
%%
/* ------------------------------------------------ C FUNCTIONS -------------------------------------------- */
void yyerror(char *message){
printf("Error: \"%s\"\t in line %d. Token = %s\n", message, line_no, yytext);
exit(1);
}
/* ------------------------------------------ MAIN FUNCTION --------------------------------------------- */
int main(int argc, char *argv[]){
hashTable = (hash *) calloc(SIZE, sizeof(hash));
int flag;
yyin = fopen(argv[1],"r");
//yyparse(): reads tokens, executes actions
flag = yyparse();
fclose(yyin);
printf("Parsing finished succesfully!\n\n");
printf(" __________________________\n");
Print();
printf(" __________________________\n");
return flag;
}
我被卡住了,不知道該怎么辦。 編譯器只是不喜歡我的代碼:
parser.y: warning: 9 nonterminals useless in grammar [-Wother]
parser.y: warning: 9 rules useless in grammar [-Wother]
parser.y:136.1-5: warning: nonterminal useless in grammar: break [-Wother]
136 | break:BREAK QM NEWLINE ;
| ^~~~~
parser.y:157.1-12: warning: nonterminal useless in grammar: end_function [-Wother]
157 | end_function: ENDFUNCTION NEWLINE;
| ^~~~~~~~~~~~
parser.y:177.1-11: warning: nonterminal useless in grammar: end_if_stmt [-Wother]
177 | end_if_stmt:ENDIF NEWLINE ;
| ^~~~~~~~~~~
parser.y:180.1-17: warning: nonterminal useless in grammar: end_for_statement [-Wother]
180 | end_for_statement: ENDFOR NEWLINE;
| ^~~~~~~~~~~~~~~~~
parser.y:188.1-10: warning: nonterminal useless in grammar: end_switch [-Wother]
188 | end_switch: ENDSWITCH NEWLINE;
| ^~~~~~~~~~
parser.y:191.1-5: warning: nonterminal useless in grammar: while [-Wother]
191 | while: WHILE L_PAR inspector_gen R_PAR NEWLINE action ;
| ^~~~~
parser.y:192.1-8: warning: nonterminal useless in grammar: end_wile [-Wother]
192 | end_wile: ENDWHILE NEWLINE;
| ^~~~~~~~
parser.y:217.1-10: warning: nonterminal useless in grammar: start_main [-Wother]
217 | start_main: STARTMAIN NEWLINE action;
| ^~~~~~~~~~
parser.y:218.1-8: warning: nonterminal useless in grammar: end_main [-Wother]
218 | end_main: ENDMAIN NEWLINE ;
| ^~~~~~~~
parser.y: warning: 48 shift/reduce conflicts [-Wconflicts-sr]
parser.y: warning: 68 reduce/reduce conflicts [-Wconflicts-rr]
parser.y:141.10-29: warning: rule useless in parser due to conflicts [-Wother]
141 | indent3: INDENT INDENT INDENT;
| ^~~~~~~~~~~~~~~~~~~~
parser.y:142.10-36: warning: rule useless in parser due to conflicts [-Wother]
142 | indent4: INDENT INDENT INDENT INDENT;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
parser.y:143.10-43: warning: rule useless in parser due to conflicts [-Wother]
143 | indent5: INDENT INDENT INDENT INDENT INDENT;
我知道我做錯了什么。 請幫我! 我不知道如何繼續。
當您開始使用 Bison 時(實際上,任何時候都在使用它),最好將語法編寫成小塊並進行調試。 對於任何編程語言的任何項目來說,這都是一個好習慣,但當你缺乏經驗時尤其如此。 不要實現所有的操作符,只實現其中的幾個。 一旦你完成了這項工作,你就可以添加其余的。 同樣,不要實現每個語句的語法。 從一個開始,讓它工作,然后添加另一個。 當干草堆不是很大時,更容易發現錯誤。 一旦你養成了這種習慣,你會發現編程實際上變得容易多了。
現在,你的實際問題。 如果從未使用過非終端,則它是“無用的”。 換句話說,如果您定義了一個非終端並且從未在某些生產中使用它,bison 會警告您定義該非終端毫無意義。 而 Bison 足夠聰明,可以遞歸地進行分析:如果非終結符出現的唯一位置是在無用的非終結符的右側,那么該非終結符也是無用的,您將收到警告它也。 (我認為這不是問題,但我沒有對您的代碼進行廣泛的分析。)
因此,例如,在您的語法中,除了將其定義為break:BREAK QM NEWLINE ;
您沒有對非終結break
執行任何操作break:BREAK QM NEWLINE ;
. 我想您打算稍后將它添加到您的語句替代方案中,在這種情況下,您可以忽略警告(這就是為什么它是警告而不是錯誤的原因)。 但是,總的來說,在您准備好添加它的用法之前,不要在語法中添加break
會產生更少的噪音。
現在,移位/減少沖突。 除非你足夠幸運地偶然發現了一個明顯的問題,否則很難在沒有看到沖突的實際狀態的情況下找出導致轉移/減少沖突的原因; 如果您使用-v
命令行選項,Bison 將生成這些狀態的報告。 在 John Levine 的優秀書中有關於調試沖突的有用信息。
最新的 Bison 版本可以通過生成反例為您提供更多幫助。 在Bison 手冊中有另一個對沖突的很好的解釋,以及一些解釋如何使用這個新功能的例子。
但是,碰巧的是,我確實偶然發現了一個明顯的錯誤。 您有(部分)以下作品:
line: action | print NEWLINE
action: INDENT line | indent2 line
indent2: INDENT INDENT
還有很多,但這足以引起沖突。 撇開INDENT
標記的構成不談,只需注意print
以標記PRINT
開頭,假設我們有以下輸入:
INDENT INDENT PRINT
現在,你的語法是如何推導出來的? 它可以這樣做:
line -> action -> INDENT line -> INDENT action
-> INDENT INDENT line -> INDENT INDENT print NEWLINE
或者它可以這樣做:
line -> action -> indent2 line -> INDENT INDENT line
-> INDENT INDENT print NEWLINE
(我希望你知道,推導步驟包括用它的右側替換一個非終結符。所以上面是同一輸入的兩個不同推導,這意味着你的語法是模棱兩可的。Bison 堅持產生一個最終解析——這就是它的全部目的——如果對同一個輸入有兩個可能的解析,它就不能這樣做。
或者,更准確地說,它可以通過在某些規則的幫助下選擇要使用的解析來做到這一點。 但是這些規則通常不會按預期工作,而且對於一個模棱兩可的語法,除了語法的作者之外,其他人真的沒有辦法知道要進行哪個解析。 因此 Bison 會警告您有 shift/reduce 沖突,然后使用其內置規則來選擇一種可能的解析策略。
通常,與您的語法一樣,當 Bison 應用這些規則時,它會發現某些產生式將不再適用於任何輸入(因為消歧規則選擇了其他一些產生式來應用)。 在這種情況下,被淘汰的產品變得毫無用處,這幾乎肯定是一個錯誤,因此 Bison 也會對此發出警告。
我不知道這是否是所有沖突的原因,但最好解決這個問題,然后看看剩下的。
在我看來,您的意圖並不是編寫一種類似 Python 的語言,其中布局決定塊結構,因為您似乎為所有塊語法定義了顯式結束標記。 不可能使用上下文無關語法來強制執行正確的縮進,所以我希望這不是您的意圖。
對於像 C 這樣不將布局視為語法一部分的語言,最常用的解析技術是詞法掃描器簡單地跳過空格(制表符和空格); 由於空格對解析沒有任何影響,因此通過強迫語法考慮空格可能會去哪里來混淆語法是沒有意義的。 這當然是我的建議,但因為我真的不知道你的意圖是什么,我真的不能再說了。
祝項目順利。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.