We are doing a calculator compiler, however, we haven't been able to run it because of file dependencies that we cannot understand.
The beginning of the yacc file is as so:
%{
#include "symtab.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
Table *t;
typedef struct {
Val value; // value
char* type; // type
char* lexeme; // lexeme
} IDAttr;
typedef struct {
Val value; // value
char* type; // type
} EAttr;
void yyerror(const char *s)
{
fprintf(stderr, "%s\n", s);
exit(1);
}
int yylex(void);
%}
%union {
char* lexeme; // lexeme
EAttr eattr; // attributes for expressions
char* type; // type
IDAttr idattr; // attributes for variables
Val value; // value
}
Val is also a union, defined in symtab.h and it looks like this:
typedef union {
int int_value;
double real_value;
char* boolean_value;
} Val;
The error is this:
make
flex calc-lex.l;
bison -vd calc-ambiguo.y;
gcc calc-ambiguo.tab.c lex.yy.c symtab.c -ly -ll
In file included from calc-lex.l:5:
calc-ambiguo.y:33:8: error: unknown type name ‘EAttr’
33 | EAttr eattr; // attributes for expressions
| ^~~~~
calc-ambiguo.y:35:8: error: unknown type name ‘IDAttr’
35 | IDAttr idattr; // attributes for variables
| ^~~~~~
calc-ambiguo.y:36:8: error: unknown type name ‘Val’
36 | Val value; // value
| ^~~
...
Changing the order of operations in the makefile didn't fix the problem. As far as I understand Bison has a problem with nesting unions/structures and that's why EAttr and so on aren't recognized by the yacc file.
As far as I understand Bison has a problem with nesting unions/structures and that's why EAttr and so on aren't recognized by the yacc file.
No, not at all. Bison itself makes no attempt to interpret your data types. It generates C source code in a fairly mechanical fashion, without understanding (or needing to) the C semantics of your data types or semantic actions. Similar for Flex. But you do need to have a certain amount of understanding of how the resulting generated artifacts will be structured and what they will contain, because it is partly on you to ensure that they are suitable for use.
The prologue section of your grammar file contains definitions of type aliases EAttr
and IDAttr
, and apparently it causes one for Val
to be #include
d. Bison copies that code into the generated parser file (calc-ambiguo.tab.c
). You should see it somewhere near the top if you examine that file in an editor.
The error messages you report are a bit confusing, because Flex and Bison go out of the way to try to make messages refer to lines of the scanner and parser definition files instead of the C source files generated from them, but note well that they start with ...
In file included from calc-lex.l:5:
This tells you that the issue is with building the scanner source (lex.yy.c
, generated by Flex), not the parser source. And you will find that the aforementioned type definitions do not appear in that file, nor in the Bison-generated calc-ambiguo.tab.h
that it presumably #include
s. On the other hand, you will find that calc-ambiguo.tab.h
does include a C union
declaration derived rather directly from the Bison %union
declaration appearing in your grammar file (go ahead, look). That union
references types EAttr
, IDAttr
, and Val
, for which no definitions have (yet) appeared in the scanner translation unit. That is the source of the problem.
Even if your scanner does not assign type EAttr
, IDAttr
, or Val
to any token it produces, it will still need to have definitions of those types. One possible solution would be to move the definitions of the EAttr
and IDAttr
structures into symtab.h
, and add code to your lexer definition to include that header before the parser header. Something along these lines:
%{
#include "symtab.h"
/* Presumably, this one already appears: */
#include "calc-ambiguo.tab.h"
%}
If that does not appeal to you then I'm sure you can come up with something that does, now that you understand the nature of the problem.
Additional notes:
As a matter of style, #include
local headers after standard library headers, not before.
Your grammar definition file should not need a forward declaration of yylex()
. There is no upside to providing one.
You do not need to link with libl
, and you probably shouldn't. The main thing it provides is a simple main()
that runs the generated scanner, but you want your parser to drive the scanner (plus, you are presumably providing your own main()
anyway). This library is intended for use in building programs from a scanner definition alone.
Similarly, you do not need to link liby
, and you probably shouldn't. The main things it provides are a yyerror()
(which you definitely are already providing yourself) and a main()
, which you are presumably providing yourself.
The member
char* boolean_value;
of your Val
union looks suspicious. If it is indeed meant to represent a boolean value, as its name suggests, then shouldn't it be type _Bool
, or possibly another integer type? Converting a string such as "true" to a boolean value is a job for the scanner. Also, transferring strings as semantic values from the scanner to the parser has several subtleties and complications that you should prefer to avoid where possible.