user3246015
user3246015

Reputation: 19

Old bison/flex code does not compile

I've inherited some really old bison code, and it's been some time since I've dealt with this, the compile fails with a number of warnings and errors, the warnings I can resolve, but I don't see how to correct the errors. Here's an example:

Given this set of definitions:

    %%
    pgm:    exp                     { pgm = $1; }

    exp:    list                    { $$ = dlink(newnode(NULL,NULL),$1); }
    |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3)); }

    list:   rec
    |       list ',' rec            { $$ = Link($1, $3); }

    rec:    /* null */              { $$ = newnode(NULL, NULL); }
    |       path
    |       path '@' NAME           { $$ = attribute($1, $3); }
    |       path '(' list ')'       { $$ = dlink($1, $3); }

    path:   NAME                    { $$ = newnode($1, NULL); }
    |       path '.' NAME           { $$ = dlink($1, newnode($3, NULL)); }
    |       path '[' INT ']'        { $$ = dlink($1, newnode($3, $3)); }
    |       path '[' INT '-' INT ']'{ $$ = dlink($1, newnode($3, $5)); }

    %%

which are compiled as:

    bison -d -y gram.y
    gcc -std=c89 -c y.tab.c

I get the following errors (this is just one of many):

    gram.y: At top level:
    gram.y:218:1: error: conflicting types for newnode
    newnode(name, range)
    ^
   gram.y:49:26: note: previous implicit declaration of newnode was here
   "exp: list   { $$ = dlink(newnode(NULL,NULL),$1); }"

Upvotes: 1

Views: 562

Answers (1)

I'll answer this question as it touches on topics that can cause confusion both for students learning to use bison/yacc and for those, like the OP, who are compiling older code and get confused where the errors come from.

As hinted in the comments, the errors are nothing to do with bison at all, but come from the C code contained within the bison files, but are not artefacts of bison, and are not caused by changes in bison or yacc over the years. They have been caused by changes in C compilers over the years. Modern C compilers are less tolerant (better) than very old ones of yore, particularly in regard to function calls and argument checking. Even when selecting options on a modern compiler to implement backward compatibility it sometimes still generates errors when a previous compiler might have said nothing or only given a warning.

To demonstrate that the faults are purely in the C, one can do the following:

Prompt> gcc -c -xc -std=c89 -
main () {
#line 49 "gram.y"
int a  = newnode(a,1);
int * b  = newnode(1,1);
}
void *
#line 218 "gram.y"
newnode(name, range)
int name,
range;
{
}
^Z
gram.y: In function 'main':
gram.y:50:12: warning: initialization makes pointer from integer without a cast
[enabled by default]

            ^
gram.y: At top level:
gram.y:218:1: error: conflicting types for 'newnode'
gram.y:49:10: note: previous implicit declaration of 'newnode' was here

          ^

You can see, in a few lines of C, gcc will output the same error message as you are seeing.

There is much on SO about this issue, and how it can be solved:

Footnote: It is possible to turn off some gcc errors, or reduce them to warnings by using a #pragma, or controlling the level of error diagnostics, but in this case it is not possible. The gcc option -Wno-error-implicit-function-declaration is not supported. The line:

#pragma GCC diagnostic warning "-Werror-implicit-function-declaration"

(or similar) also does not eliminate the error. Just mentioning it in case anyone was wondering

Type Conflicts in Bison

Many people when working with similar code in bison/yacc do have problems with type mismatches in their code that are detected by bison. It is probably worth adding some notes on this to make the answer more complete. It is often the case that when parsing something similar to your grammar, one makes a parse tree using the functions dlink, Link, newnode and attribute. The tree would be made of some struct and linked through pointers to struct struct * like this:

struct treeNode {
    int  item1,item2;
    struct treeNode *left;
    struct treeNode *right;
  };

typedef  struct treeNode TREE_NODE;
typedef  TREE_NODE        *BINARY_TREE;

To avoid the problems of implicit function types shown earlier, we can declare prototypes for the functions like this:

BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
BINARY_TREE newnode(int item1, int item2);
BINARY_TREE attribute(BINARY_TREE left, int item);
BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);

We can combine this with your grammar to make this file:

%{
#define NULL 0
struct treeNode {
    int  item1,item2;
    struct treeNode *left;
    struct treeNode *right;
  };

typedef  struct treeNode TREE_NODE;
typedef  TREE_NODE        *BINARY_TREE;

BINARY_TREE pgm;
BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
BINARY_TREE newnode(int item1, int item2);
BINARY_TREE attribute(BINARY_TREE left, int item);
BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);
%}

%token INT NAME

%%
pgm:    exp                     { pgm = $1; }

exp:    list                       {    $$ = dlink(newnode(NULL,NULL),$1); }
    |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3)); }

list:   rec
    |       list ',' rec            { $$ = Link($1, $3); }

rec:    /* null */              { $$ = newnode(NULL, NULL); }
    |       path
    |       path '@' NAME           { $$ = attribute($1, $3); }
    |       path '(' list ')'       { $$ = dlink($1, $3); }

path:   NAME                    { $$ = newnode($1, NULL); }
    |       path '.' NAME           { $$ = dlink($1, newnode($3, NULL)); }
    |       path '[' INT ']'        { $$ = dlink($1, newnode($3, $3)); }
    |       path '[' INT '-' INT ']'{ $$ = dlink($1, newnode($3, $5)); }

%%

May be what you have is something like that? If we put that through bison and gcc we get many warnings, but it does generate code. I note you said you had many warnings. Perhaps they were like these:

gram.y: In function 'yyparse':
gram.y:23:11: warning: assignment makes pointer from integer without a cast [enabled by default]
 pgm: exp                     { pgm = $1; }
           ^
gram.y:25:5: warning: passing argument 2 of 'dlink' makes pointer from integer without a cast [enabled by default]
 exp:    list                       {    $$ = dlink(newnode(NULL,NULL),$1); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:25:18: warning: assignment makes integer from pointer without a cast [enabled by default]
 exp:    list                       {    $$ = dlink(newnode(NULL,NULL),$1); }
                  ^
gram.y:26:5: warning: passing argument 2 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3)); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
gram.y:25:18: warning: assignment makes integer from pointer without a cast [enabled by default]
 exp:    list                       {    $$ = dlink(newnode(NULL,NULL),$1); }
                  ^
gram.y:26:5: warning: passing argument 2 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3
)); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:26:5: warning: passing argument 1 of 'Link' makes pointer from integer without a cast [enabled by default]
     |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3
)); }
     ^
gram.y:17:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:26:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3
)); }
               ^
gram.y:29:5: warning: passing argument 1 of 'Link' makes pointer from integer without a cast [enabled by default]
     |       list ',' rec            { $$ = Link($1, $3); }
     ^
gram.y:17:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:29:5: warning: passing argument 2 of 'Link' makes pointer from integer without a cast [enabled by default]
     |       list ',' rec            { $$ = Link($1, $3); }
     ^
gram.y:17:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:29:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       list ',' rec            { $$ = Link($1, $3); }
               ^
gram.y:31:15: warning: assignment makes integer from pointer without a cast [enabled by default]
 rec:    /* null */              { $$ = newnode(NULL, NULL); }
               ^
gram.y:33:5: warning: passing argument 1 of 'attribute' makes pointer from integer without a cast [enabled by default]
     |       path '@' NAME           { $$ = attribute($1, $3); }
     ^
gram.y:16:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE attribute(BINARY_TREE left, int item);
             ^
gram.y:33:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       path '@' NAME           { $$ = attribute($1, $3); }
               ^
gram.y:34:5: warning: passing argument 1 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       path '(' list ')'       { $$ = dlink($1, $3); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:34:5: warning: passing argument 2 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       path '(' list ')'       { $$ = dlink($1, $3); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:34:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       path '(' list ')'       { $$ = dlink($1, $3); }
               ^
gram.y:36:15: warning: assignment makes integer from pointer without a cast [enabled by default]
 path:   NAME                    { $$ = newnode($1, NULL); }
               ^
gram.y:37:5: warning: passing argument 1 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       path '.' NAME           { $$ = dlink($1, newnode($3, NULL)); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:37:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       path '.' NAME           { $$ = dlink($1, newnode($3, NULL)); }
               ^
gram.y:38:5: warning: passing argument 1 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       path '[' INT ']'        { $$ = dlink($1, newnode($3, $3)); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:38:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       path '[' INT ']'        { $$ = dlink($1, newnode($3, $3)); }
               ^
gram.y:39:5: warning: passing argument 1 of 'dlink' makes pointer from integer without a cast [enabled by default]
     |       path '[' INT '-' INT ']'{ $$ = dlink($1, newnode($3, $5)); }
     ^
gram.y:14:13: note: expected 'BINARY_TREE' but argument is of type 'YYSTYPE'
 BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
             ^
gram.y:39:15: warning: assignment makes integer from pointer without a cast [enabled by default]
     |       path '[' INT '-' INT ']'{ $$ = dlink($1, newnode($3, $5)); }
               ^

These can be very common for new users of bison. To eliminate these warnings it is necessary to inform bison on how the types are being used to enable it to generate type-correct code. This is done by the addition of a couple more lines to the bison code. First we have to tell it the types that can be returned by grammar rule actions:

%union {
  BINARY_TREE tVal;
  int iVal;
  }

and then we have to tell it the types returned for individual tokens and rule actions:

%token <iVal> NAME INT
%type <tVal> exp list rec path

If we insert those into the grammar file all the errors and warnings are eliminated, and we get:

%{
#define NULL 0
struct treeNode {
    int  item1,item2;
    struct treeNode *left;
    struct treeNode *right;
  };

typedef  struct treeNode TREE_NODE;
typedef  TREE_NODE        *BINARY_TREE;

BINARY_TREE pgm;
BINARY_TREE dlink(BINARY_TREE left, BINARY_TREE right);
BINARY_TREE newnode(int item1, int item2);
BINARY_TREE attribute(BINARY_TREE left, int item);
BINARY_TREE Link(BINARY_TREE left, BINARY_TREE right);
%}
%union {
  BINARY_TREE tVal;
  int iVal;
  }

%token <iVal> NAME INT
%type <tVal> exp list rec path 

%%
pgm:    exp                     { pgm = $1; }

exp:    list                       {    $$ = dlink(newnode(NULL,NULL),$1); }
    |       exp ';' list            { $$ = Link($1, dlink(newnode(NULL,NULL),$3)); }

list:   rec
    |       list ',' rec            { $$ = Link($1, $3); }

rec:    /* null */              { $$ = newnode(NULL, NULL); }
    |       path
    |       path '@' NAME           { $$ = attribute($1, $3); }
    |       path '(' list ')'       { $$ = dlink($1, $3); }

path:   NAME                    { $$ = newnode($1, NULL); }
    |       path '.' NAME           { $$ = dlink($1, newnode($3, NULL)); }
    |       path '[' INT ']'        { $$ = dlink($1, newnode($3, $3)); }
    |       path '[' INT '-' INT ']'{ $$ = dlink($1, newnode($3, $5)); }

%%

I hope I identified some of the aspects of type conflicts in C code that can affect users new to bison or those adopting someone else's code base.

Upvotes: 2

Related Questions