I am writing a basic program with yacc/lex, and I get an undefined symbol error
in the yacc
C generated source code for yyerror
and yylex
. How do I specify that those files are generated in the lex
lexer?
Here is what I have tried:
readfile_grammar.y
%{
#include <time.h>
#include <stdio.h>
%}
%union {
struct tm datetime; /* DATE TIME VALUES */
double float_number; /* 8 BYTES DOUBLE VALUE */
long integer_number; /* 8 BYTES INTEGER VALUE */
}
%token <datetime> DATETIME
%token <float_number> FLOAT_NUMBER
%token <integer_number> INTEGER_NUMBER
%%
lastbid_lastask: DATETIME ',' FLOAT_NUMBER ',' FLOAT_NUMBER ',' INTEGER_NUMBER { printf("MATCH %lf %lf %ld\n", $3, $5, $7); }
;
%%
int main(int argc, char *argv[]) {
yyparse();
return 0;
}
readfile_lexer.l
%{
#include <time.h>
#include "readfile_grammar.h"
%}
%%
/* SKIP BLANKS AND TABS */
[\t ] { ; }
/* YYYYMMDD HHMM DATE */
[12][09][0-9][0-9][0-1][0-9][0-3][0-9][ ][0-2][0-9][0-5][0-9] { read_date_YYYYMMDD_HHMM(); return DATETIME; }
/* FLOAT NUMBER */
[0-9]+\.[0-9]+ { read_float_number(); return FLOAT_NUMBER; }
/* INTEGER NUMBER */
[0-9]+ { read_integer_number(); return INTEGER_NUMBER; }
%%
/* READ FLOAT NUMBER */
void read_float_number(void) {
sscanf(yytext, "%lf", &yylval.float_number);
}
/* READ INTEGER NUMBER */
void read_integer_number(void) {
sscanf(yytext, "%ld", &yylval.integer_number);
}
/* READ YYYYMMDD HHMM DATE */
void read_date_YYYYMMDD_HHMM(void) {
/* DATETIME STRUCT TM */
struct tm dt;
char buffer[80];
/* READ VALUES */
sscanf(yytext, "%4d%2d%2d %2d%2d", &dt.tm_year, &dt.tm_mon, &dt.tm_day, &dt.tm_hour, &dt.tm_min);
/* NORMALIZE VALUES */
dt.tm_year = dt.tm_year - 1900; /* NORMALIZE YEAR */
dt.tm_mon = dt.tm_mon - 1; /* NORMALIZE MONTH */
dt.tm_isdst = -1; /* NO INFORMATION ABOUT DST */
mktime(&dt); /* NORMALIZE STRUCT TM */
/* PRINT DATETIME */
strftime(buffer, 80, "%c %z %Z\n", &dt);a
printf("%s\n", buffer);
/* COPY STRUCT TM TO YACC RETURN VALUE */
memcpy(&dt, &yylval.datetime, sizeof(dt));
}
makefile
CCFLAGS = -std=c89
YFLAGS = -d # Forces generation of y.tab.h
OBJS = readfile_lexer.o readfile_grammar.o
TARGET = readfile
readfile: $(OBJS)
cc $(OBJS) -std=c89 -lm -o $(TARGET)
readfile_grammar.h readfile_grammar.o: readfile_grammar.y
yacc $(YFLAGS) -oreadfile_grammar.c readfile_grammar.y
cc $(CCFLAGS) readfile_grammar.c
readfile_lexer.o: readfile_lexer.l readfile_grammar.h
lex -oreadfile_lexer.c readfile_lexer.l
cc $(CCFLAGS) readfile_lexer.c
clean:
rm -f $(OBJS) readfile_grammar.[ch] readfile_lexer.c
While trying to compile the source code I get the mentioned error:
% make
yacc -d -oreadfile_grammar.c readfile_grammar.y
cc -std=c89 readfile_grammar.c
ld: error: undefined symbol: yylex
>>> referenced by readfile_grammar.c
>>> /tmp/readfile_grammar-2d51ee.o:(yyparse)
ld: error: undefined symbol: yyerror
>>> referenced by readfile_grammar.c
>>> /tmp/readfile_grammar-2d51ee.o:(yyparse)
ld: error: undefined symbol: yylex
>>> referenced by readfile_grammar.c
>>> /tmp/readfile_grammar-2d51ee.o:(yyparse)
ld: error: undefined symbol: yyerror
>>> referenced by readfile_grammar.c
>>> /tmp/readfile_grammar-2d51ee.o:(yyparse)
cc: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1
Stop.
In your makefile, in the command cc $(CCFLAGS) readfile_grammar.c
add the -c
option to specify that this is just one module and should be compiled to a .o
file:
readfile_grammar.h readfile_grammar.o: readfile_grammar.y
yacc $(YFLAGS) -oreadfile_grammar.c readfile_grammar.y
cc -c $(CCFLAGS) readfile_grammar.c
Otherwise cc assumes it's the entire program and tries to link it into an executable binary, which of course fails because it calls functions that are in other files.