• Keine Ergebnisse gefunden

Part 2: Basics

2.3 Implementation Meta Tools

2.3.4 Make

The final step is to put all the programs together. We already had the tools kimwitu, yacc and lex and we also need a C-compiler to produce executables of our specification. However, it is not easy to find out which tool to call in order to get the appropriate output. Figure 8 below depicts the dependencies that we have already with the files mentioned in the previous sections. Please note that kc is the name of the kimwitu tool.

cc (4)

ebnf-lex.l ebnf-parse.y ebnf-*.k

lex (1) yacc (2´) kc (3)

ebnf-lex.c

y.tab.h

ebnf-parse.c

ebnf-parse.output

ebnf

<kc-gen>.c

ebnf-parse.o ebnf-lex.o

ebnf-*.c

<kc-gen>.o ebnf-*.o

cc (4) cc (4)

cc/ld (5)

cc (4) ebnf.c

ebnf.o cc (4)

Figure 8: File Dependencies

Please note that this simple example does not fully follow the methodology as introduced in Section 2.2 because front end and back end are merged into one tool.

The program make is designed to ease the task of defining dependencies between programs. The idea is to simply state what the dependencies are and make will itself care for all of them. In our case, some of the dependencies are already built-in into make. make does already know how to create a .c-file provided it has a .y file (it uses yacc then) or how to create a .c file from a .l file. Other dependencies have to be given explicitely.

In Figure 8 the different kinds of dependencies that have to be handled by make are numbered. They are implemented as follows: (1), (2) and (4) are already built-in into make. It is only necessary to state what the concrete names are for lex, yacc and cc and which options to use for them. We also define a variable WINNT to indicate if the makefile is used under cygnus and a variable SHELL to denote the standard shell.

WINNT=1

# Tools ifndef WINNT SHELL = /bin/sh endif

YACC = bison #-v CC = g++ -g KC = kc++

LEX = flex

ifdef WINNT

EXE = .exe# # postfix for executables endif

# Flags

YFLAGS = -d -y LFLAGS = -t

CFLAGS = -Wall -DYYDEBUG -DYYERROR_VERBOSE

Furthermore, we introduce abbreviations for the lex, yacc and kimwitu input files and for the final program.

# Sources

KFILES = ebnf-abstract.k ebnf-semantics.k ebnf-trans.k ebnf-pretty.k YFILE = ebnf-parse.y

LFILE = ebnf-lex.l

EBNF = ebnf${EXE}

Now we start with the first real make rule. The first rule is special, because make tries to build the object that is on its left hand side when called without arguments. We do only insert a dummy target here to print out the correct usage.

# default rule

notknown: ; @echo "try make all"

all: ${EBNF}

So it remains to formulate the dependencies (3) and (5). Kimwitu is a really intelligent tool. It will not change its output files when they would be generated the same way as they already are. This is good when the programs afterwards are concerned, because the cc input files are not changed and hence cc will not be called. However, this way the output files of kimwitu could be younger than its input files and still be up to date. make would not notice this because it just checks the file time stamps. In order to avoid this, a time stamp file for the kimwitu call is introduced. Kimwitu has to be called whenever this time stamp is outdated with respect to the kimwitu input files. This is formulated below.

The first lines of the text below introduce abbreviations for the kimwitu generated files and the auxiliary timestamp file for kimwitu.

KC_TIME = .kc_time_stamp

KC_OGEN = k.o csgiok.o unpk.o rk.o KC_OSRC = ${KFILES:.k=.o}

KOBJS = ${KC_OGEN} ${KC_OSRC}

# Kimwitu compilation (note: kc does not touch unchanged files)

${KC_TIME}: ${KFILES}

${KC} ${KFILES}

date > ${KC_TIME}

It is straightforward to formulate the dependency (5), see below.

${EBNF}: %${EXE}: ${KC_TIME} ${LFILE:.l=.o} ${YFILE:.y=.o} ${KOBJS} %.o

${CC} ${CFLAGS} -o $@ ${LFILE:.l=.o} ${YFILE:.y=.o} ${KOBJS} $*.o

Now the dependencies are almost complete. However, we have to consider also dependencies between .c-Files and .h-Files. The most difficult one of those is that ebnf-lex.c includes y.tab.h which in turn is generated by yacc. Here the situation is the other way round then with kimwitu above: yacc will always generate y.tab.h, even when it is the same as before. To handle this case properly, we introduce an extra file ebnf-parse.h which is constructed from y.tab.h. Both files are compared and only in case of a difference ebnf-parse.h is updated. Instead of including y.tab.h we now use ebnf-parse.h for inclusion in ebnf-lex.c. Please note, that for constructing y.tab.h it suffices to construct ebnf-parse.c, because y.tab.h is generated also.

# the normal lex/yacc header trick

${YFILE:.y=.h} : y.tab.h; -cmp -s $@ $< || cp $< $@

y.tab.h : ${YFILE:.y=.c}

Finally, all dependencies between .c and .h files can be generated automatically using the C compiler. They are stored in a file named .depend.

depend: ${KC_TIME} ${LFILE:.l=.c} ${YFILE:.y=.c} ${YFILE:.y=.h}

${CC} -MM *.c > .depend

@echo .depend is included

The file .depend is included into the makefile if it is existing, otherwise a warning message is generated.

DEPEND=${wildcard .depend}

ifneq "${DEPEND}" ""

include .depend else

Makefile: MM

MM: ; @echo "**************** You must make depend first *******************"

endif

This concludes the makefile. The EBNF main file

It remains to present the main file for the EBNF analyser, which calls all the elements introduced before.

1. #include <stdio.h>

2. #include "k.h"

3. #include "rk.h"

4. #include "unpk.h"

5. extern int yyparse();

6. extern void init_symtab();

7. /* The syntax tree root */

8. syntax TheSyntax;

9. extern symtab TheSymtab;

10. void printer_f(const char *s, uview_enum v) { printf("%s", s); } 11. void dummy_printer_f(const char *s, uview_enum v) {}

12. int main()

13. { KC_Printer printer(printer_f), dummy_printer(dummy_printer_f);

14. fprintf(stderr,"EBNF analysis\n");

15. if (!yyparse()) 16. { init_symtab();

17. TheSyntax->unparse( dummy_printer, create_symtab );

18. TheSymtab->unparse( dummy_printer, check_symtab );

19. TheSyntax = TheSyntax->rewrite( basic_rewrite );

20. TheSyntax->unparse(printer, pretty);

21. TheSymtab->unparse(printer, pretty);

22. return 0;

23. } else return 1;

24. } /* main */

Lines 1-9: Declare all the external parts that are generated by lex, yacc and kimwitu.

Lines 10-11: Introduce two printing functions: one really printing and one without output (see also lines 17-18).

Lines 15-21: Process the input according to the methodology.

Im Dokument Formal Semantics for SDL (Seite 49-52)