Search code examples
compiler-constructionyacclex

Yacc and Lex error


I'm doing an exercise using Yacc and Lex. The exercise is this:

image

After created and compiled file .y and .lex i have no error with these 2 commands:

bison -vd -o parser.c es.y
flex es.lex

After these i compile the parser.c with:

gcc -g -c parser.c

and i have these errors:

In function yyparse:
parser.c:1304: error: incompatible types in assignment
parser.c:1334: error: incompatible types in assignment 
parser.c:1436: error: incompatible types in assignment
parser.c:1576: error: incompatible types in assignment

and other warnings. My lex file is this:

%{
#include "parser.h"
#include "def.h"
Value lexval;
%}
%option noyywrap
delimiter       [ \t\n]
spacing         {delimiter}+
digit           [0-9]
num             {digit}+
id              [a-zA-Z]+
sugar           [()*+=;]
%%
{spacing}       ;
{sugar}         {return(yytext[0]);}
if              {return(IF);}
else            {return(ELSE);}
then            {return(THEN);}
end             {return(END);}
write           {return(WRITE);}
{id}            {lexval.name = newstring(yytext); return(ID);}
{num}           {lexval.val=atoi(yytext); return(NUM);}
.               {return(ERROR);}
%%
char *newstring(char *s)
{
  char *p;
  p = malloc(sizeof(strlen(s)+1));
  strcpy(p, s);
  return(p);
}

my yacc file is:

%{
#include "def.h"
#define YYSTYPE struct{char *name; int val;}
#define NIL -1
extern Value lexval;
struct SymbTab{char label[30];int value;};
struct SymbTab tab[1000];
int val;
int size=0;
%}
%token ID NUM IF THEN ELSE END WRITE ERROR
%%
program : stat_list
             ;

stat_list : stat ';' stat_list
       | stat
       ;

stat : assign_stat
       | write_stat
       ;

assign_stat : ID {$$.name = lexval.name;} '=' expr {assign($2.name, $4.val);}
                  ;

expr : expr '+' term {$$.val = $1.val + $3.val;}
       | term {$$.val = $1.val;}
       ;

term : term '*' factor {$$.val = $1.val * $3.val;}
       | factor {$$.val = $1.val;}
       ;

factor : '(' expr ')' {$$.val = $2.val;}
       | if_expr {$$.val = $1.val;}
       | ID {if((val =lookup(lexval.name)) == NIL) error(); else $$.val = val;}
       | NUM {$$.val = lexval.val;}
       ;

if_expr : IF expr THEN expr ELSE expr END {$$.val = ($2.val ? $4.val : $6.val);}
       ;

write_stat : WRITE expr {printf("%d\n", $2.val);}
        ;
%%
int isPresent(char *lab)
{
    int i;
    for(i=0; i<size; i++)
        if(strcmp(tab[i].label,lab)==0)
            return i;
    return -1000;
}
void assign(char *l,int n)
{
    if(isPresent(l)==-1000)
    {
        strcpy(tab[size].label,l);
        tab[size].value=n;
        size++;
    }
    else
        tab[isPresent(l)].value=n;
}
int lookup(char *lab)
{
    int i;
    for(i=0; i<size; i++)
        if(strcmp(tab[i].label,lab)==0)
            return tab[i].value;
    return NIL;
}

void error(){ fprintf(stderr, "Syntax error\n"); }


int main(){ yyparse(); return 0; }

and my def.h is:

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

char *newstring(char*),
     *strcpy(char*, const char*);

void error(),assign(char *l,int n);

int lookup(char *lab),isPresent(char *lab),yylex(),main();

typedef union
{
    int val;
    char *name;
} Value;

I don't know how to resolve the errors that i get in parser.c


Solution

  • The immediate problem is this:

    #define YYSTYPE struct{char *name; int val;}
    

    Here's a minimal example of what happens with the macro definition; you can try it with your C compiler:

    #define YYSTYPE struct{char *name; int val;}
    int main(void) {
      YYSTYPE a = {"", 42};
      YYSTYPE b;
      b = a;
      return 0;
    }
    

    I'm unable to reproduce that error message with any version of gcc I have handy. Both gcc 6.x and gcc 7.x produce the more meaningful error message (along with a large number of warnings):

    struct.c:5:5: error: incompatible types when assigning to type ‘struct <anonymous>’ from type ‘struct <anonymous>’
    

    Even so, the error needs a bit of explanation. Since YYSTYPE is a macro, the above code is exactly equivalent to the following in which the macro has been expanded:

    int main(void) {
      struct{char *name; int val;} a = {"", 42};
      struct{char *name; int val;} b;
      b = a;
      return 0;
    }
    

    In this snippet, a and b are of different types, because in C, every occurrence of an anonymous struct (that is, a struct without a tagname) is a distinct type.

    If the declarations of a and b had been in different files compiled separately, there would not be a problem, since the types of a and b are compatible. But in the same file, in order for aggregate assignment to be valid, the types of the value and the target variable must be the same, not just compatible. Since they are not the same, an error is produced.

    The simple fix is to make YYSTYPE a type alias rather than a macro:

    typedef struct{char *name; int val;} YYSTYPE;
    

    Type aliases are compile-time identifiers for a particular type; that type is still an anonymous struct but now every declaration of a variable as YYSTYPE is the same anonymous struct.

    But that's not enough for bison/yacc, because you also have to tell it that you are providing your own definition of YYSTYPE. Otherwise, it will insert its own definition (which is a type-alias for int), and that will create a different compiler error because YYSTYPE will have two incompatible definitions. So you need to also include the following rather odd-looking macro definition:

    #define YYSTYPE YYSTYPE
    

    If YYSTYPE is defined as a macro, then bison/yacc's default definition will not be included. (It's protected with #ifdef YYSTYPE.) The apparently recursive definition is not a problem because the C preprocessor only substitutes a given name once; once it replaces YYSTYPE with YYSTYPE, it won't try the replacement again.


    The assignment itself seems to me to betray a woeful lack of knowledge about the yacc/lex toolset. The use of lexval instead of using the standard yylval mechanism creates a number of issues for the grammar; for example, it forces the use of a mid-rule action in your assignment production. I have no idea why you were asked to do that, and I suspect that much of whatever else you are being taught about yacc and lex is also incorrect.