6c60bfd77fdc8488ef52b87cf41e7cd1ca0e2d4d kent Thu Aug 1 18:28:19 2013 -0700 Main ra parsing function is generate and passes visual inspection. Haven't tried compiling it yet though. diff --git src/utils/raToStructGen/raToStructGen.c src/utils/raToStructGen/raToStructGen.c new file mode 100644 index 0000000..e8024a4 --- /dev/null +++ src/utils/raToStructGen/raToStructGen.c @@ -0,0 +1,261 @@ +/* raToStructGen - Write C code that will read/write a C structure from a ra file.. */ +#include "common.h" +#include "linefile.h" +#include "hash.h" +#include "options.h" +#include "obscure.h" +#include "sqlNum.h" +#include "ra.h" +#include "asParse.h" + +char *requiredAsComma = NULL; + +void usage() +/* Explain usage and exit. */ +{ +errAbort( + "raToStructGen - Write C code that will read/write a C structure from a ra file.\n" + "usage:\n" + " raToStructGen guide.as output.c\n" + "options:\n" + " -required=comma,sep,list - comma separated list of required fields.\n" + ); +} + +/* Command line validation table. */ +static struct optionSpec options[] = { + {"required", OPTION_STRING}, + {NULL, 0}, +}; + +struct raToStructReader +/* Something to help us parse RAs into C structures. */ + { + struct xyzReader *next; + char *name; /* Name of structure */ + int fieldCount; /* Number of fields. */ + char **fields; /* Names of all fields - not allocated here. */ + char **requiredFields; /* Names of required fields - not allocated here */ + int requiredFieldCount; /* Count of required fields. */ + struct hash *fieldIds; /* So we can do hashLookup/switch instead of strcmp chain */ + int *requiredFieldIds; /* An array of IDs of required fields. */ + bool *fieldsObserved; /* An entry for each field we've observed. */ + }; + +struct raToStructReader *raToStructReaderNew(char *name, int fieldCount, char **fields, + int requiredFieldCount, char **requiredFields) +/* Create a helper object for parsing an ra file into a C structure. This structure will + * contain */ +{ +struct raToStructReader *reader; +AllocVar(reader); +reader->name = cloneString(name); +reader->fieldCount = fieldCount; +reader->fields = fields; +reader->requiredFieldCount = requiredFieldCount; +reader->requiredFields = requiredFields; +struct hash *fieldIds = reader->fieldIds = hashNew(4); +int i; +for (i=0; i<fieldCount; ++i) + hashAddInt(fieldIds, fields[i], i); +if (requiredFieldCount > 0) + { + AllocArray(reader->requiredFieldIds, requiredFieldCount); + for (i=0; i<requiredFieldCount; ++i) + { + char *required = requiredFields[i]; + struct hashEl *hel = hashLookup(fieldIds, required); + if (hel == NULL) + errAbort("Required field %s is not in field list", required); + reader->requiredFieldIds[i] = ptToInt(hel->val); + } + } +AllocArray(reader->fieldsObserved, fieldCount); +return reader; +} + +void raToStructReaderFree(struct raToStructReader **pReader) +/* Free up memory associated with reader. */ +{ +struct raToStructReader *reader = *pReader; +if (reader != NULL) + { + freeMem(reader->name); + freeHash(&reader->fieldIds); + freeMem(reader->fieldIds); + freeMem(reader->fieldsObserved); + freez(pReader); + } +} + +void raToStructReaderCheckRequiredFields(struct raToStructReader *reader, struct lineFile *lf) +/* Make sure that all required files have been seen in the stanza we just parsed. */ +{ +int *requiredFieldIds = reader->requiredFieldIds; +bool *fieldsObserved = reader->fieldsObserved; +int i; +for (i=0; i<reader->requiredFieldCount; ++i) + { + if (!fieldsObserved[requiredFieldIds[i]]) + { + errAbort("Required field %s not found line %d of %s\n", reader->requiredFields[i], + lf->lineIx, lf->fileName); + } + } +} + + +void raToStructGen(char *guideFile, char *outFileC) +/* raToStructGen - Write C code that will read/write a C structure from a ra file. */ +{ +int requiredCount = 0; +char **requiredFields = NULL; +if (requiredAsComma != NULL) + { + if (lastChar(requiredAsComma) == ',') + trimLastChar(requiredAsComma); + requiredCount = chopByChar(requiredAsComma, ',', NULL, 0); + AllocArray(requiredFields, requiredCount); + chopByChar(requiredAsComma, ',', requiredFields, requiredCount); + } +struct asObject *as = asParseFile(guideFile); +if (as == NULL) + errAbort("Nothing in %s", guideFile); +if (as->next != NULL) + errAbort("Multiple objects in %s, only one allowed", guideFile); + +uglyf("%d of %d fields of %s are required\n", requiredCount, slCount(as->columnList), as->name); +FILE *f = mustOpen(outFileC, "w"); + +/* Print out start of parsing function. */ +fprintf(f, +"\n" +"struct %s *%sFromNextRaStanza(struct lineFile *lf, struct raToStructReader *reader)\n" +"/* Return next stanza put into an %s. */\n" +"{\n" +"if (!raSkipLeadingEmptyLines(lf, NULL))\n" +" return NULL;\n" +"\n" +"struct %s *el;\n" +"AllocVar(el);\n" +"\n" +"bool *fieldsObserved = reader->fieldsObserved;\n" +"bzero(fieldsObserved, reader->fieldCount);\n" +"\n" +"char *tag, *val;\n" +"while (raNextTagVal(lf, &tag, &val, NULL))\n" +" {\n" +" struct hashEl *hel = hashLookup(reader->fieldIds, tag);\n" +" if (hel != NULL)\n" +" {\n" +" int id = ptToInt(hel->val);\n" +" if (fieldsObserved[id])\n" +" errAbort(\"Duplicate tag %%s line %%d of %%s\\n\", tag, lf->lineIx, lf->fileName);\n" +" fieldsObserved[id] = TRUE;\n" +" switch (id)\n" +" {\n" +, as->name, as->name, as->name, as->name); + + +/* Now loop through and print out cases for each field. */ +struct asColumn *col; +int colIx = 0; +for (col = as->columnList; col != NULL; col = col->next) + { + fprintf(f, " case %d:\n", colIx++); + struct asTypeInfo *lt = col->lowType; + enum asTypes type = lt->type; + if (col->isList) + { + switch (type) + { + /* Handle numerical types */ + case t_float: + case t_double: + case t_int: + case t_uint: + case t_short: + case t_ushort: + case t_off: + case t_string: + case t_lstring: + if (col->linkedSizeName) + { + fprintf(f, " int %sSize;\n", col->name); + fprintf(f, " sql%sDynamicArray(val, &el->%s, &%sSize);\n", + lt->listyName, col->name, col->name); + fprintf(f, " if (!fieldsObserved[%d])\n", + asColumnFindIx(as->columnList, col->linkedSizeName)); + fprintf(f, " errAbort(\"%s must precede %s\");\n", + col->linkedSizeName, col->name); + fprintf(f, " if (%sSize != el->%s)\n", + col->name, col->linkedSizeName); + fprintf(f, " errAbort(\"%s has wrong count\");\n", + col->name); + } + else if (col->fixedSize) + fprintf(f, " sql%sArray(val, el->%s, %d);\n", + lt->listyName, col->name, col->fixedSize); + else + internalErr(); + break; + default: + errAbort("Sorry, %s array column is too complex for this program", col->name); + break; + } + } + else + { + switch (type) + { + /* Handle numerical types */ + case t_float: + case t_double: + case t_int: + case t_uint: + case t_short: + case t_ushort: + case t_off: + fprintf(f, " el->%s = sql%s(val);\n", col->name, lt->nummyName); + break; + case t_string: + case t_lstring: + fprintf(f, " el->%s = cloneString(val);\n", col->name); + break; + default: + errAbort("Sorry, %s column is too complex for this program", col->name); + break; + } + } + fprintf(f, " break;\n"); + } + +/* Print out end of parsing function. */ +fprintf(f, +" default:\n" +" internalErr();\n" +" break;\n" +" }\n" +" }\n" +" }\n" +"\n" +"if (reader->requiredFieldIds != NULL)\n" +" raToStructReaderCheckRequiredFields(reader, lf);\n" +"return el;\n" +"}\n" +"\n" +); + +carefulClose(&f); +} + +int main(int argc, char *argv[]) +/* Process command line. */ +{ +optionInit(&argc, argv, options); +if (argc != 3) + usage(); +requiredAsComma = optionVal("required", NULL); +raToStructGen(argv[1], argv[2]); +return 0; +}