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;
+}