Bison shift/reduce conflict / reduce/reduce conflict warnings

风格不统一 提交于 2019-12-19 04:55:08

问题


When i run this bison code in Ubuntu Linux i get these warnings : 1shift/reduce conflict [-Wconflicts-sr] 2 reduce/reduce conflicts [-Wcolficts-sr]

Here's a screenshot for more clarity: http://i.imgur.com/iznzSsn.png

Edit: reduce/reduce errors are in line 86 : typos_dedomenwn line 101: typos_synartisis

and the shift/reduce error is in: line 129: entoli_if

I can't find how to fix them could someone help?

Here's the bison code bellow :

        %{

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>

        int totalerrors=0;

        extern int yylex();
        extern FILE *yyin;
        extern int lineno; //Arithmos grammis pou kanei parse

        //error handling
        void yyerror(const char *msg) {
        }
        //filling the error array
        void printError(char y[],int x){
            //param 1: error string
            //param 2: line number
            char temp[15];
            char temp2[5];
            char final[256];
            sprintf(temp2,"%d: ",x);
            strcpy(temp, "In Line ");
            strcat(temp,temp2);
            strcpy(final,"");
            strcat(final,temp);
            strcat(final,y);
            printf("%d) %s\n",totalerrors+1,final);
            totalerrors++;
        }
        %}
        %start start
        %token T_sigkritikos_telestis
        %token T_typos_dedomenwn
        %token T_typos_synartisis
        %token T_stathera
        %token T_newline
        %token T_kefalida_programmatos
        %token T_extern
        %token T_void
        %token T_return
        %token T_if
        %token T_else
        %token T_plus
        %token T_minus
        %token T_mult
        %token T_div
        %token T_percentage
        %token T_int
        %token T_bool
        %token T_string
        %token T_true
        %token T_false
        %token T_id
        %token T_semic
        %token T_comma
        %token T_openpar
        %token T_closepar
        %token T_ampersand
        %token T_begin
        %token T_end
        %token T_excl
        %token T_or
        %token T_equals
        %token T_semileft
        %token T_semiright
        %%
        start: exwterikes_dilwseis T_kefalida_programmatos tmima_orismwn tmima_entolwn;

        exwterikes_dilwseis: exwteriko_prwtotypo exwterikes_dilwseis
            | ;

        exwteriko_prwtotypo: T_extern prwtotypo_synartisis;

        tmima_orismwn: orismos tmima_orismwn
            | ;

        orismos: orismos_metavlitwn
            | orismos_synartisis
            | prwtotypo_synartisis;

        orismos_metavlitwn: typos_dedomenwn lista_metavlitwn T_semic;

        typos_dedomenwn: T_int
            | T_bool
            | T_string;

        loop1: T_comma T_id
            | ;

        lista_metavlitwn: T_id loop1;

        orismos_synartisis: kefalida_synartisis tmima_orismwn tmima_entolwn;

        prwtotypo_synartisis: kefalida_synartisis T_semic;

        kefalida_synartisis: typos_synartisis T_id T_openpar lista_typikwn_parametrwn T_closepar
            | typos_synartisis T_id T_openpar T_closepar;

        typos_synartisis: T_int
            | T_bool
            | T_void;

        lista_typikwn_parametrwn: typikes_parametroi loop2;

        loop2: T_comma typikes_parametroi
            | ;

        typikes_parametroi: typos_dedomenwn T_ampersand T_id;

        tmima_entolwn: T_begin loop3 T_end;

        loop3: entoli loop3
            | ;

        entoli: apli_entoli T_semic
            | domimeni_entoli
            | sintheti_entoli;

        sintheti_entoli: T_semileft loop3 T_semiright;

        domimeni_entoli: entoli_if;

        apli_entoli: anathesi 
            | klisi_sunartisis
            | entoli_return
            | ;

        entoli_if: T_if T_openpar geniki_ekfrasi T_closepar entoli else_clause 
            | T_if T_openpar geniki_ekfrasi T_closepar entoli;

        else_clause: T_else entoli;

        anathesi: T_id T_equals geniki_ekfrasi;

        klisi_sunartisis: T_id T_openpar lista_pragmatikwn_parametrwn T_closepar 
            | T_id T_openpar T_closepar;

        lista_pragmatikwn_parametrwn: pragmatiki_parametros loop4;

        loop4: T_semic pragmatiki_parametros loop4
            | ;

        pragmatiki_parametros: geniki_ekfrasi;

        entoli_return: T_return geniki_ekfrasi 
            | T_return;

        geniki_ekfrasi: genikos_oros loop5;

        loop5: T_or T_or genikos_oros loop5
            | ;

        genikos_oros: genikos_paragontas loop6;

        loop6: T_ampersand T_ampersand loop6 
            | ;

        genikos_paragontas: T_excl genikos_protos_paragontas
            | genikos_protos_paragontas;

        genikos_protos_paragontas: apli_ekfrasi tmima_sigrisis
            | apli_ekfrasi;

        tmima_sigrisis: T_sigkritikos_telestis apli_ekfrasi;



        apli_ekfrasi: aplos_oros loop7;

        loop7: T_plus aplos_oros loop7
            | T_minus aplos_oros loop7
            | ;

        aplos_oros: aplos_paragontas loop8;

        loop8: T_mult aplos_paragontas loop8
            | T_div aplos_paragontas loop8
            | T_percentage aplos_paragontas loop8
            | ;

        aplos_paragontas: T_plus aplos_prot_oros
            | T_minus aplos_prot_oros
            | aplos_prot_oros;

        aplos_prot_oros: T_id
            | stathera
            | klisi_sunartisis
            | T_openpar geniki_ekfrasi T_closepar;

        stathera: T_true
            |T_false;

        %%
        int main(int argc, char *argv[]){
            ++argv; --argc;  //agnooume to onoma tou exe
            if (argc==1) {
                FILE *fp = fopen(argv[0],"r");
                if (fp!=NULL) {
                    printf("Reading input from file: %s\n",argv[0]);
                    printf("Output:\n\n");
                    yyin = fp;
                    yyparse();
                } else {
                    printf("File doesn't exist\n");
                    return 1;
                }
            } else if (argc>1) {
                printf("Only one file allowed for input...\n");
                return 1;
            } else {
                printf ("Parsing from stdin..\n");
                yyparse();
            }
            if (totalerrors==0) {
                printf("All good!\n");
                printf("===================================\n");
                printf("Parsing complete! No errors found!!\n");
            } else {
                printf("===================================\n");
                printf("Total Errors: %d\n",totalerrors);
            }
            return 0;
        }

回答1:


A. Redundant non-terminals

The reduce/reduce conflicts are because you have two non-terminals which exist only to gather together different types:

typos_dedomenwn: T_int
    | T_bool
    | T_string;

typos_synartisis: T_int
    | T_bool
    | T_string;

Where these non-terminals are used, it is impossible for the parser to know which one applies; it cannot tell until further along in the declaration. However, it doesn't matter. You could just define a single typos non-terminal, and use it throughout:

typos: T_int
    | T_bool
    | T_string;

orismos_metavlitwn: typos lista_metavlitwn T_semic;
kefalida_synartisis: typos T_id T_openpar lista_typikwn_parametrwn T_closepar
    | typos T_id T_openpar T_closepar;
typikes_parametroi: typos T_ampersand T_id;

B. Dangling else

The shift/reduce conflict is the classic problem with "C" style if statements. These statements are difficult to describe in a way which is not ambiguous. Consider:

if (expr1) if (expr2) statement1; else statement2;

We know that the else must match the second if, so the above is equivalent to:

if (expr1) { if (expr2) statement1; else statement2; }

But the grammar also matches the other possible parse, equivalent to:

if (expr1) { if (expr2) statement1; } else statement2;

There are three possible solutions to this problem:

  1. Do nothing. Bison does the right thing here, by design: it always prefers "shift" over "reduce". What that means is that if an else could match an open if statement, bison will always do that, rather than holding onto the else to match some outer if statement. There is a pretty good description of this in the Dragon book, amongst other places.

    The problem with this solution is that you still end up with a warning about shift/reduce conflicts, and it is hard to distinguish between "OK" conflicts, and newly-created "not OK" conflicts. Bison provides the %expect declaration so you can tell it how many conflicts you expect, which will suppress the warning if the right number are found, but that is still pretty fragile.

  2. Use precedence declarations. These are described in the bison manual. and their use in solving the dangling else problem is a running example in that chapter. In your case, it would look something like this:

    %precedence T_then  /* Fake terminal, needed for %prec */
    %precedence T_else
     /* ... */
    %%
     /* ... */
    
    entoli_if: T_if T_openpar geniki_ekfrasi Tw_closepar entoli T_else entoli
       | T_if T_openpar geniki_ekfrasi T_closepar entoli %prec T_then
    

    Here, I have eliminated the unnecessary non-terminal else_clause because it hides the else token. If you wanted to keep it, for whatever reason, you would need to add a %prec T_else to the end of the entoli_if production which uses it.

    The %precedence declaration is only available from bison 3.0 onwards. If you have an earlier version of bison, you can use the %nonassoc declaration instead, but this may hide some other errors.

  3. Fix the grammar. It is actually possible to make an unambiguous grammar, but it is a bit of work.

    The important point is that in:

    if (expr) statement1 else statement2
    

    statement1 cannot be an unmatched if statement. If statement1 is an if statement, it must include an else clause; otherwise, the else in the outer if would match the inner if. And that applies recursively to any trailing statements in statement1, such as

    if (e2) statement2; 
      else if (e3) statement3
      else /* must be present */ statement;
    

    We can express this by dividing statements into "matching" statements (where all if are matched by else) and "non-matching" statements: (I haven't tried to preserve the greek non-terminal names here; sorry. You'll have to adapt the idea to your grammar).

    statement: matching_statement | non_matching_statement ;
    matching_statement: call_statement | assignment_statement | ...
        | matching_if_statement
    non_matching_statement: non_matching_if_statement
        /* might be others, see below */
    
    if_condition: "if" '(' expression ')' ;
    
    matching_if_statement:
          if_condition matching_statement "else" matching_statement ;
    non_matching_if_statement:
          if_condition statement
        | if_condition matching_statement "else" non_matching_statement
        ; 
    

    In C, there are other compound statements which can end with a statement (while, for). Each of these will also have a "matching" and "non-matching" version, depending on whether the final statement is matching or non-matching:

    while_condition: "while" '(' expression ')' ;
    matching_while_statement: while_condition matching_statement ;
    non_matching_while_statement: while_condition non_matching_statement ;
    

    As far as I can see, this does not apply to your language, but you might want to extend it in the future to include such statements.

C. Some notes about bison style

  1. Bison allows you to use single character tokens as themselves, surrounded by single quotes. So instead of declaring T_openpar and then writing verbose rules which use it, you can just write '('; you don't even need to declare it. (In your flex -- or other -- scanner, you would just return '('; instead of return T_openpar, which is why you don't need to declare the token.) This usually makes grammars more readable.

  2. Bison also lets you specify a human-readable name for a token. (This feature is not in all yacc derivatives, but it is pretty common.), which can also make grammars more readable. For example, you can give names to the if and else tokens as follows:

    %token T_if "if"
    %token T_else "else"
    

    and then you could use the quoted strings in your grammar rules. (I did that in my last example for the dangling-else problem.) In the flex scanner, you still need to use the token symbols T_if and T_else.

  3. If you have a two-symbol token like &&, it is usually better if the scanner recognizes it and returns a single token, instead of the parser recognizing two consecutive & tokens. In the second case, the parser will recognize:

    boolean_expr1 &  & boolean_expr2
    

    as though it had been written

    boolean_expr1 && boolean_expr2
    

    although the first one was most likely an error which should be reported.

  4. Bison is a bottom-up LALR(1) parser generator. It is not necessary to remove left-recursion. Bottom-up parsers prefer left-recursion, and left-recursive grammars are usually more accurate and easier to read. For example, it is better all round to declare:

    apli_ekfrasi: aplos_oros
        | apli_ekfrasi '+' aplos_oros
        | apli_ekfrasi '-' aplos_oros;
    

    than to use LL-style repeated suffixes (loop7 in your grammar). The left-recursive grammar can be parsed without extending the parser stack, and more accurately represents the syntactic structure of the expression, making parser actions easier to write.

    There are a number of other places in your grammar which you might want to revisit.

    (This advice comes straight from the bison manual: "you should always use left recursion, because it can parse a sequence of any number of elements with bounded stack space.")



来源:https://stackoverflow.com/questions/32821775/bison-shift-reduce-conflict-reduce-reduce-conflict-warnings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!