I'm doing an exercise using Yacc and Lex. The exercise is this:
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
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.