=================================== Auto-generated Code for Token Kinds [TOC] =================================== The source code for _strTokenKind()_ and the enum constants for _enum TokenKind_ can be generated from a text file that contains in each line the identifier for a token kind. In __Quiz 22__ you wrote a program for that. This now can be integrated into the project. By modifying the makefile we can make sure that these source files always will be up to date. :links: Quiz 22 -> doc:session20/page02 Solution for Quiz 22 (With Some Improvement) ============================================ On Unix system newline are encode with single character '\n' (ASCII 10). In the Windows world two characters are used '\r' (ASCII 13) and '\n'. In the material provided for Quiz 22 only the Unix case was handled correctly. Line are read with _getline()_ and then the last character (the character before the null byte) overwritten with a null byte. So for a Unix or Windows text files this overwrites the \n'. For a Windows text file we actually have to overwrite the second last character (the '\r') character. ---- CODE (file=session21/make_tokenkind.c, fold) ------------------------------ #include #include void printEnumHeader(FILE *out) { fprintf(out, "enum TokenKind\n"); fprintf(out, "{\n"); } void printEnumFooter(FILE *out) { fprintf(out, "};\n"); } void printStrHeader(FILE *out) { fprintf(out, "const char *\n"); fprintf(out, "strTokenKind(enum TokenKind tokenKind)\n"); fprintf(out, "{\n"); fprintf(out, "%*sswitch (tokenKind) {\n", 4, ""); } void printStrFooter(FILE *out) { fprintf(out, "%*sdefault:\n", 4, ""); fprintf(out, "%*sfprintf(stderr, \"internal error in strTokenKind: " "tokenKind = %%d\",", 8, ""); fprintf(out, "%*stokenKind);\n", 4, ""); fprintf(out, "%*sfinalizeExit(1);\n", 4, ""); fprintf(out, "%*sreturn \"\";\n", 4, ""); fprintf(out, "%*s}\n", 4, ""); fprintf(out, "}\n"); } void usage(const char *prg) { fprintf(stderr, "usage: %s input output1 outpu2\n", prg); exit(1); } int main(int argc, char *argv[]) { if (argc != 4) { usage(argv[0]); } FILE *in = fopen(argv[1], "r"); FILE *outEnum = fopen(argv[2], "w"); FILE *outStr = fopen(argv[3], "w"); if (!in) { fprintf(stderr, "can not open input file '%s'\n", argv[1]); } if (!outEnum) { fprintf(stderr, "can not open output file '%s'\n", argv[2]); } if (!outStr) { fprintf(stderr, "can not open output file '%s'\n", argv[3]); } printEnumHeader(outEnum); printStrHeader(outStr); char *line = 0; size_t capacity = 0; ssize_t len; while ((len = getline(&line, &capacity, in)) > 0) { line[len - 2] == '\r' ? (line[len - 2] = 0) : (line[len - 1] = 0); fprintf(outEnum, "%*s%s,\n", 4, "", line); fprintf(outStr, "%*scase %s:\n", 4, "", line); fprintf(outStr, "%*sreturn \"%s\";\n", 8, "", line); } free(line); printEnumFooter(outEnum); printStrFooter(outStr); fclose(in); fclose(outEnum); fclose(outStr); } -------------------------------------------------------------------------------- My Status of the ABC Project ============================ ---- SHELL (path=session21/git, hide) ------------------------------------------ rm -rf abc git clone git@gitlab.com:uni-ulmulm-university-department-of-numerical-analysis/abc.git git config --global advice.detachedHead false -------------------------------------------------------------------------------- ---- SHELL (path=session21/git/abc, hide) -------------------------------------- git checkout auto-gen6 -------------------------------------------------------------------------------- In my project the makefile was modified so that program with the prefix _xgen_ are considered to generate source files that are required. Hence, the program ~make_tokenkind.c~ was renamed into ~xgen_tokenkind.c~. The source files that need to be generated can be specified in a variable. Here you can see that first the executable ~xgen_tokenkind~ get created and then form _tokenkind.txt_ the source files ~gen_tokenkind.h~ and ~gen_strtokenkind.c~ are generated. If ~xgen_tokenkind.c~ or ~tokenkind.txt~ will be changed then the generated source files will be updated and all object files and targets that depend on the generated source files. ---- SHELL (path=session21/git/abc) -------------------------------------------- make -------------------------------------------------------------------------------- Here all files from my project at the current status: :import: session21/git/abc/expr.c [fold] :import: session21/git/abc/expr.h [fold] :import: session21/git/abc/finalize.c [fold] :import: session21/git/abc/finalize.h [fold] :import: session21/git/abc/lexer.c [fold] :import: session21/git/abc/lexer.h [fold] :import: session21/git/abc/Makefile [fold] :import: session21/git/abc/parser.c [fold] :import: session21/git/abc/parser.h [fold] :import: session21/git/abc/README.md [fold] :import: session21/git/abc/str.c [fold] :import: session21/git/abc/str.h [fold] :import: session21/git/abc/sym.c [fold] :import: session21/git/abc/sym.h [fold] :import: session21/git/abc/test_calc.in [fold] :import: session21/git/abc/test_lexer.in [fold] :import: session21/git/abc/tokenkind.c [fold] :import: session21/git/abc/tokenkind.h [fold] :import: session21/git/abc/tokenkind.txt [fold] :import: session21/git/abc/ustr.c [fold] :import: session21/git/abc/ustr.h [fold] :import: session21/git/abc/xgen_tokenkind.c [fold] :import: session21/git/abc/xtest_calc.c [fold] :import: session21/git/abc/xtest_expr.c [fold] :import: session21/git/abc/xtest_finalize.c [fold] :import: session21/git/abc/xtest_lexer.c [fold] :import: session21/git/abc/xtest_lexer_sanity.c [fold] :import: session21/git/abc/xtest_sym.c [fold] :import: session21/git/abc/xtest_ustr.c [fold]