Programming Utilities Guide

Compiling lex and yacc Sources

lex and yacc produce C source files as output. Source files for lex end in the suffix .l, while those for yacc end in .y. When used separately, the compilation process for each is similar to that used to produce programs from C sources alone.

There are implicit rules for compiling the lex or yacc sources into .c files; from there, the files are further processed with the implicit rules for compiling object files from C sources. When these source files contain no #include statements, there is no need to keep the .c file, which in this simple case serves as an intermediate file. In this case you could use .l.o rule, or the .y.o rule, to produce the object files, and remove the (derived) .c files.

For example, the makefile:

CFLAGS= -O 
.KEEP_STATE:

all: scanner parser 
scanner: scanner.o 
parser: parser.o

produces the result shown below.

$ make -n
rm -f scanner.c 
lex -t scanner.l > scanner.c 
cc -O -c -o scanner.o scanner.c 
rm -f scanner.c
yacc parser.y 
cc -O -c -o parser.o y.tab.c 
rm -f y.tab.c

Things become more complicated when you use lex and yacc in combination. In order for the object files to work together properly, the C code from lex must include a header produced by yacc. It might be necessary to recompile the C source file produced by lex when the yacc source file changes. In this case, it is better to retain the intermediate (.c) files produced by lex, as well as the additional .h file yacc provides, to avoid running lex whenever the yacc source changes.


Note -

yacc produces output files named y.tab.c and y.tab.h. If you want the output files to have the same basename as the source file, you must rename them.


The following makefile maintains a program built from a lex source, a yacc source, and a C source file.

CFLAGS= -O 
.KEEP_STATE:

a2z: c_functions.o scanner.o parser.o 
        	cc -o $@ c_functions.o scanner.o parser.o 
scanner.c: 

parser.c + parser.h: parser.y 
        	yacc -d parser.y 
        	mv y.tab.c parser.c 
        	mv y.tab.h parser.h

Because there is no transitive closure for implicit rules, you must supply a target entry for scanner.c. This entry bridges the gap between the .l.c implicit rule and the .c.o implicit rule, so that the dependency list for scanner.o extends to scanner.l. Since there is no rule in the target entry, scanner.c is built using the .l.c implicit rule.

The next target entry describes how to produce the yacc intermediate files. Because there is no implicit rule for producing both the header and the C source file using yacc -d, a target entry must be supplied that includes a rule for doing so.