簡體   English   中英

Bison 非終結符在語法中無用,規則在解析器中無用

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM