GNU Gengen 1.1

A parameterized-text-generator generator based on a template

by Lorenzo Bettini


Table of Contents



Gengen (GENerator GENerator) is a tool that, starting from a parameterized text, called template, generates a text generator that can substitute parameters with values.

At the moment Gengen can generate C++ or C code; however other target languages are under development (e.g., Java).

Gengen is free software; you are free to use, share and modify it under the terms of the GNU General Public License (see COPYING).

The home page of Gengen is http://www.gnu.org/software/gengen

See NEWS file for a summary of new features in this release and ChangeLog for the complete list of changes sources

You can also find details about new features on my blog, in this area: http://tronprog.blogspot.com/search/label/gengen.

Download

You can download it from GNU's ftp site:
ftp://ftp.gnu.org/gnu/gengen/ or from one of its mirrors (see http://www.gnu.org/prep/ftp.html).

I do not distribute Windows binaries anymore; since, they can be easily built by using Cygwin C/C++ compiler, available at http://www.cygwin.com/. However, if you don't feel like downloading such compiler, you can request such binaries directly to me, by e-mail (please find my e-mail address at my home page) and I can send them to you.

The steps to perform for the installation are standard for packages in source form: once you have unpacked the sources in a directory, `cd' to the directory containing the package's source code and execute the following steps:

./configure
make
make install
Note: unless you specify a different install directory by --prefix option of configure (e.g. ./configure --prefix=<your home>), you must be root to 'make install'.

Anonymous CVS Access

This project's CVS repository can be checked out through anonymous (pserver) CVS with the following instruction:
cvs -z3 -d:pserver:anonymous@cvs.savannah.gnu.org:/sources/gengen co gengen 
Further instructions can be found at the address: http://savannah.gnu.org/projects/gengen .

Please notice that this way you will get the latest development sources of Gengen, which may also be unstable. This solution is the best if you intend to correct/extend this program: you should send me patches against the latest cvs repository sources.

If, on the contrary, you want to get the sources of a given release, through cvs, say, e.g., version X.Y.Z, you must specify the tag rel_X_Y_Z when you run the cvs command or the cvs update command.

When you compile the sources that you get through the cvs repository, before running the configure and make commands, you should, at least the first time, run the command:

     sh reconf

This will run the autotools commands in the correct order, and also copy possibly missing files. You should have installed recent versions of automake and autoconf in order for this to succeed. You will also need flex and bison.

Simple example

Say you are writing a C/C++ program and at some point your program has to generate the following code:
     if (i < 10)
       printf("the value of i is %d", i);

It is not so difficult to write this piece of C++ code:

     cout << "if (i < 10)" << endl;
     cout << "  printf(\"the value of i is %d\", i);" << endl;

or the C code:

     printf("if (i < 10)\n");
     printf("  printf(\"the value of i is %%d\", i);\n");

provided that you remember to escape the " (and in the C code, also the %).

Suppose now that the previous piece of code has to be generated many times by your program, and every time instead of i another symbol has to be generated (decided at run time). In this case, supposing that this value is contained in a variable symb, the code for generating this code would be a little bit more complex:

     cout << "if (" << symb << "< 10)" << endl;
     cout << "  printf(\"the value of " << symb << " is %d\", "
          << symb << ");" << endl;

And the C version would be even more obfuscated.

Probably you didn't even realize that you forgot to leave a space before the < 10; basically this is due to the fact that this piece of code mixes the code that has to be generated with the code that generates it, and this tends to make this part of program less easy to maintain. Especially if some day you have to change the code that has to be generated, you'll have to act on this part of the program, and probably you'll have to execute some tests in order to be sure that you did it right.

If the code that you have to generate is a slightly more complex, the task may easily become a pain in the neck!

Wouldn't it be nice if you could write the code to be generated in a separate file, let's call it template, say test1.cc_skel this way

     if (@i@ < 10)
       printf("the value of @i@ is %d", @i@);

and have a tool that generates a generator, that you can instantiate at run-time with the value that has to be substituted to the parameter i? If such a tool existed, and it generated a file test1_c.h with a C struct test1_gen_struct, then you could write simply this code, in another file, say test1_gen_c.c:

     #include <stdio.h>
     
     #include "test1_c.h"
     
     int
     main()
     {
       struct test1_gen_struct gen_struct;
       gen_struct.i = "foo";
       generate_test1(stdout, &gen_struct, 0);
       printf("\n");
       gen_struct.i = "bar";
       generate_test1(stdout, &gen_struct, 0);
       printf("\n");
     
       return 0;
     }
     

Alternatively, if it generated a file test1.h with a C++ class test1_gen_class, then you could write simply this code, in another file, say test1_gen.cc:

     #include <iostream>
     
     using std::cout;
     using std::endl;
     
     #include "test1.h"
     
     int
     main()
     {
       test1_gen_class gen_class;
       gen_class.set_i("foo");
       gen_class.generate_test1(cout);
       cout << endl;
       gen_class.set_i("bar");
       gen_class.generate_test1(cout);
       cout << endl;
     
       return 0;
     }
     

and when you run it you would obtain the expected output:

     if (foo < 10)
       printf("the value of foo is %d", foo);
     if (bar < 10)
       printf("the value of bar is %d", bar);

Well, Gengen does right this! Now the code that has to be generated and the code that generates it are separated and they can be maintained more easily: if you want to change the code that has to be generated you act on the file test1.cc_skel; alternatively, say you need to change the value that will be substituted for i, you just change the file test1_gen.cc or test1_gen_c.c.

Notice that the method generate_test1 accepts an output stream (indeed in this example the standard output stream cout is used), thus the stream abstraction facilities can be exploited. Similarly, the C function generate_test1 accepts a FILE*, so you can use the C file abstraction.

Indeed in order to generate the C++ file test1.h with the class test1_gen_class, I simply had to run the following command:

     gengen -i test1.cc_skel --file-name test1.h --gen-name test1

and in order to generate the C file test1_c.h with the structure test1_gen_struct, I simply had to run the following command:

     gengen -i test1.cc_skel --file-name test1_c.h --gen-name test1 \
             --output-format=c

If I caught your attention and you would like to know more about these options and more advanced features of Gengen, I hope you read on :-)

Gengen manual

For installation and all the advanced features of Gengen and more involved examples, please read the manual that comes both in info version and html version.