autoSql and autoXml: Code Generators from the Genome Project

These tools have saved us from the drudger of writing tens of thousands of lines of repetitive code—we hope you find them useful.
A Simple Example

Imagine a simple address book that just stores name, street address, zip code and state. The autoSql specification for this would be:

table addressBook
"A simple address book"
   string name;
     "Name - first, last, both, we don't care"
   lstring address;  "Street address"
   string city;  "City"
   uint zipCode; "A zip code
     is always positive, so can be unsigned"
   char[2] state;
     "Just store the abbreviation for the state"

If this looks like a bit of a hybrid between a C structure and an SQL table definition, it's because Jim was switching between C and SQL when he made the autoSql language.

When you run the address book template through autoSql, the program produces the SQL table definition:

   #A simple address book
   CREATE TABLE addressBook (
   name varchar(255) not null,    # Name -
       first, last, both, we don't care
   address longblob not null,     # Street address
   city varchar(255) not null,    # City
   zipCode int unsigned not null, # A zip code is
       always positive, so can be unsigned
   state char(2) not null,        # Just store
       the abbreviation for the state
   PRIMARY KEY(name)

and the following C structure definition:

struct addressBook
/* A simple address book */
    struct addressBook *next;
      /* Next in singly linked list. */
    char *name;
      /* Name - first, last, both, we don't care */
    char *address;    /* Street address */
    char *city;       /* City */
    unsigned zipCode; /* A zip code is always
      positive, so can be unsigned */
    char state[3];    /* Just store
      the abbreviation for the state */
Typically in C you access a single row of an SQL database at a time. The row is returned as an array of strings. It is up to the C program to convert the ASCII representation of numbers to binary numbers. This is not hard work, but after you've typed in 20 or 30 lines that look something like:
   point->x = atoi(row[1]);
   point->y = atoi(row[2]);
you'll appreciate the following two routines that autoSql generates for you:
    void addressBookStaticLoad(char **row,
          struct addressBook *ret);
    /* Load a row from addressBook table into ret. */
    /* The contents of ret will be replaced at the */
    /* next call to this function. */
    struct addressBook *addressBookLoad(char **row);
    /* Load a addressBook from row fetched with */
    /* select * from addressBook from database.*/
    /* Dispose of this with addressBookFree(). */
The first routine typically is used when you just want to process one item at a time. It doesn't allocate any dynamic memory, and so it's quite fast. The second routine saves the structure to dynamic memory. Since the C structure always includes a “next” field, you easily use this routine to build a list of address book entries.

The only problem with using dynamic memory is that you have to remember to free it. While autoSql can't remember to free things for you, it can generate routines to free a single dynamically allocated structure, or a list of dynamically allocated structures. That's what the next two routines do:

    void addressBookFree(struct addressBook **pEl);
    /* Free a single dynamically allocated
     * addressBook such as created with
     * addressBookLoad(). */
    void addressBookFreeList(
       struct addressBook **pList);
    /* Free a list of dynamically
     * allocated addressBook's */

Reading structures without having to write code to load them up a field at a time is nice, but sometimes you need to write structures, too. autoSql assumes that you'll either want to save the structure in a tab-delimited or in a comma-delimited format. It generates a routine that can do either:

    void addressBookOutput(struct addressBook *el,
       FILE *f, char sep, char lastSep);
    /* Print out addressBook. Separate fields with
     * sep. Follow last field with lastSep. */
and macros that make it convenient to do commas or tabs:
    #define addressBookTabOut(el,f)
    /* Print out addressBook as a line in a
     * tab-separated file. */
    #define addressBookCommaOut(el,f)
    /* Print out addressBook as a comma
     * separated list including final comma. */
autoSql generates a routine to read comma-separated lists. While you are unlikely to call this routine directly yourself, fields more complicated than simple strings or integers get saved in the database as comma-separated lists. This routine allows autoSql to have objects that contain other objects.