cc9a6a6dcdca9811dfeb5ac6d9f588e836407f01
larrym
  Tue Apr 17 23:16:38 2012 -0700
add jsonParse; refactor some existing code to accomodate parsed data
diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c
index 5720a2e..dcf2124 100644
--- src/hg/lib/jsHelper.c
+++ src/hg/lib/jsHelper.c
@@ -467,30 +467,57 @@
 void jsEndCollapsibleSection()
 /* End the collapsible <TR id=...>. */
 {
 puts("</TD></TR>");
 }
 
 struct jsonStringElement *newJsonString(char *str)
 {
 struct jsonStringElement *ele;
 AllocVar(ele);
 ele->str = cloneString(str);
 ele->type = jsonString;
 return ele;
 }
 
+struct jsonBooleanElement *newJsonBoolean(boolean val)
+{
+struct jsonBooleanElement *ele;
+AllocVar(ele);
+ele->val = val;
+ele->type = jsonBoolean;
+return ele;
+}
+
+struct jsonNumberElement *newJsonNumber(long val)
+{
+struct jsonNumberElement *ele;
+AllocVar(ele);
+ele->val = val;
+ele->type = jsonNumber;
+return ele;
+}
+
+struct jsonDoubleElement *newJsonDouble(double val)
+{
+struct jsonDoubleElement *ele;
+AllocVar(ele);
+ele->val = val;
+ele->type = jsonDouble;
+return ele;
+}
+
 struct jsonHashElement *newJsonHash(struct hash *h)
 {
 struct jsonHashElement *ele;
 AllocVar(ele);
 ele->type = jsonHash;
 ele->hash = h;
 return ele;
 }
 
 struct jsonListElement *newJsonList(struct slRef *list)
 {
 struct jsonListElement *ele;
 AllocVar(ele);
 ele->type = jsonList;
 ele->list = list;
@@ -500,91 +527,75 @@
 void jsonHashAdd(struct jsonHashElement *h, char *name, struct jsonElement *ele)
 {
 if (h == NULL)  // If hash isn't provided, assume global
     {
     if (jsonGlobalsHash == NULL)
         jsonGlobalsHash = newJsonHash(newHash(8));
     h = jsonGlobalsHash;
     }
 hashReplace(h->hash, name, ele);
 }
 
 void jsonHashAddString(struct jsonHashElement *h, char *name, char *val)
 {
 // Add a string to a hash which will be used to print a javascript object;
 // existing values are replaced.
-val = javaScriptLiteralEncode(val);
-char *str = needMem(strlen(val) + 3);
-sprintf(str, "\"%s\"", val);
-freez(&val);
-jsonHashAdd(h, name, (struct jsonElement *) newJsonString(str));
+jsonHashAdd(h, name, (struct jsonElement *) newJsonString(val));
 }
 
 void jsonHashAddNumber(struct jsonHashElement *h, char *name, long val)
 {
 // Add a number to a hash which will be used to print a javascript object;
 // existing values are replaced.
-char buf[256];
-safef(buf, sizeof(buf), "%ld", val);
-jsonHashAdd(h, name, (struct jsonElement *) newJsonString(buf));
+jsonHashAdd(h, name, (struct jsonElement *) newJsonNumber(val));
 }
 
 void jsonHashAddDouble(struct jsonHashElement *h, char *name, double val)
 {
 // Add a number to a hash which will be used to print a javascript object;
 // existing values are replaced.
-char buf[256];
-safef(buf, sizeof(buf), "%.10f", val);
-jsonHashAdd(h, name, (struct jsonElement *) newJsonString(buf));
+jsonHashAdd(h, name, (struct jsonElement *) newJsonDouble(val));
 }
 
 void jsonHashAddBoolean(struct jsonHashElement *h, char *name, boolean val)
 {
 // Add a boolean to a hash which will be used to print a javascript object;
 // existing values are replaced.
-jsonHashAdd(h, name, (struct jsonElement *) newJsonString(val ? "true" : "false"));
+jsonHashAdd(h, name, (struct jsonElement *) newJsonBoolean(val));
 }
 
 void jsonListAdd(struct slRef **list, struct jsonElement *ele)
 {
 struct slRef *e;
 AllocVar(e);
 e->val = ele;
 slAddHead(list, e);
 }
 
 void jsonListAddString(struct slRef **list, char *val)
 {
-val = javaScriptLiteralEncode(val);
-char *str = needMem(strlen(val) + 3);
-sprintf(str, "'%s'", val);
-freez(&val);
-jsonListAdd(list, (struct jsonElement *) newJsonString(str));
+jsonListAdd(list, (struct jsonElement *) newJsonString(val));
 }
 
 void jsonListAddNumber(struct slRef **list, long val)
 {
-char buf[256];
-safef(buf, sizeof(buf), "%ld", val);
-jsonListAdd(list, (struct jsonElement *) newJsonString(buf));
+jsonListAdd(list, (struct jsonElement *) newJsonNumber(val));
 }
 
 void jsonListAddDouble(struct slRef **list, double val)
 {
-char buf[256];
-safef(buf, sizeof(buf), "%.10f", val);
-jsonListAdd(list, (struct jsonElement *) newJsonString(buf));
+jsonListAdd(list, (struct jsonElement *) newJsonDouble(val));
 }
 
 void jsonListAddBoolean(struct slRef **list, boolean val)
 {
 jsonListAdd(list, (struct jsonElement *) newJsonString(val ? "true" : "false"));
 }
 
 static char *makeIndentBuf(int indentLevel)
 {
 if (indentLevel < 0)
     return "";
 char *indentBuf;
 indentBuf = needMem(indentLevel + 1);
 memset(indentBuf, '\t', indentLevel);
 indentBuf[indentLevel] = 0;
@@ -633,31 +644,51 @@
         if(rec->list)
             {
             for (el = rec->list; el != NULL; el = el->next)
                 {
                 struct jsonElement *val = (struct jsonElement *) el->val;
                 hPrintf("%s%s", indentBuf,tab);
                 jsonPrintRecurse(val, indentLevel);
                 hPrintf("%s%s", el->next == NULL ? "" : ",",nl);
                 }
             }
         hPrintf("%s]", indentBuf);
         break;
         }
     case jsonString:
         {
-        hPrintf("%s", ((struct jsonStringElement *) json)->str);
+        char *val = javaScriptLiteralEncode(((struct jsonStringElement *) json)->str);
+        hPrintf("\"%s\"", val);
+        break;
+        }
+    case jsonBoolean:
+        {
+        hPrintf("%s", ((struct jsonBooleanElement *) json)->val ? "true" : "false");
+        break;
+        }
+    case jsonNumber:
+        {
+        char buf[256];
+        safef(buf, sizeof(buf), "%ld", ((struct jsonNumberElement *) json)->val);
+        hPrintf("%s", buf);
+        break;
+        }
+    case jsonDouble:
+        {
+        char buf[256];
+        safef(buf, sizeof(buf), "%.10f", ((struct jsonDoubleElement *) json)->val);
+        hPrintf("%s", buf);
         break;
         }
     default:
         {
         errAbort("jsonPrintRecurse; invalid type: %d", json->type);
         break;
         }
     }
 if (indentLevel >= 0)
     freez(&indentBuf);
 }
 
 void jsonPrint(struct jsonElement *json, char *name, int indentLevel)
 {
 // print out a jsonElement, indentLevel -1 means no indenting
@@ -695,15 +726,165 @@
 }
 
 void jsonErrPrintf(struct dyString *ds, char *format, ...)
 //  Printf a json error to a dyString for communicating with ajax code; format is:
 //  {"error": error message here}
 {
 va_list args;
 va_start(args, format);
 dyStringPrintf(ds, "{\"error\": \"");
 struct dyString *buf = newDyString(1000);
 dyStringVaPrintf(buf, format, args);
 dyStringAppend(ds, javaScriptLiteralEncode(dyStringCannibalize(&buf)));
 dyStringPrintf(ds, "\"}");
 va_end(args);
 }
+
+static void skipLeadingSpacesWithPos(char *s, int *posPtr)
+/* skip leading white space. */
+{
+for (;;)
+    {
+    char c = s[*posPtr];
+    if (!isspace(c))
+	return;
+    (*posPtr)++;
+    }
+}
+
+static void getSpecificChar(char c, char *str, int *posPtr)
+{
+// get specified char from string or errAbort
+if(str[*posPtr] != c)
+    errAbort("Unexpected character '%c' (expected '%c') - string position %d\n", str[*posPtr], c, *posPtr);
+(*posPtr)++;
+}
+
+static struct jsonElement *jsonParseExpression(char *str, int *posPtr);
+
+static struct jsonElement *jsonParseObject(char *str, int *posPtr)
+{
+struct hash *h = newHash(0);
+getSpecificChar('{', str, posPtr);
+while(str[*posPtr] != '}')
+    {
+    int i;
+    skipLeadingSpacesWithPos(str, posPtr);
+    getSpecificChar('"', str, posPtr);
+    for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++)
+        ;
+    char *name = cloneStringZ(str + *posPtr, i);
+    *posPtr += i;
+    getSpecificChar('"', str, posPtr);
+
+    skipLeadingSpacesWithPos(str, posPtr);
+    getSpecificChar(':', str, posPtr);
+    skipLeadingSpacesWithPos(str, posPtr);
+
+    hashAdd(h, name, jsonParseExpression(str, posPtr));
+    skipLeadingSpacesWithPos(str, posPtr);
+    if(str[*posPtr] == ',')
+        (*posPtr)++;
+    else
+        break;
+    }
+skipLeadingSpacesWithPos(str, posPtr);
+getSpecificChar('}', str, posPtr);
+return (struct jsonElement *) newJsonHash(h);
+}
+
+static struct jsonElement *jsonParseList(char *str, int *posPtr)
+{
+struct slRef *list = NULL;
+getSpecificChar('[', str, posPtr);
+while(str[*posPtr] != ']')
+    {
+    struct slRef *e;
+    AllocVar(e);
+    skipLeadingSpacesWithPos(str, posPtr);
+    e->val = jsonParseExpression(str, posPtr);
+    slAddHead(&list, e);
+    skipLeadingSpacesWithPos(str, posPtr);
+    if(str[*posPtr] == ',')
+        (*posPtr)++;
+    else
+        break;
+    }
+skipLeadingSpacesWithPos(str, posPtr);
+getSpecificChar(']', str, posPtr);
+slReverse(&list);
+return (struct jsonElement *) newJsonList(list);
+}
+
+static struct jsonElement *jsonParseString(char *str, int *posPtr)
+{
+int i;
+getSpecificChar('"', str, posPtr);
+for(i = 0; str[*posPtr + i] && str[*posPtr + i] != '"'; i++)
+    ;
+char *val = cloneStringZ(str + *posPtr, i);
+*posPtr += i;
+getSpecificChar('"', str, posPtr);
+return (struct jsonElement *) newJsonString(val);
+}
+
+static struct jsonElement *jsonParseBoolean(char *str, int *posPtr)
+{
+struct jsonBooleanElement *ele;
+int i;
+for(i = 0; str[*posPtr + i] && isalpha(str[*posPtr + i]); i++);
+    ;
+char *val = cloneStringZ(str + *posPtr, i);
+if(sameWord(val, "true"))
+    ele = newJsonBoolean(TRUE);
+else if(sameWord(val, "false"))
+    ele =  newJsonBoolean(FALSE);
+else
+    errAbort("Invalid boolean value '%s'; pos: %d", val, *posPtr);
+*posPtr += i;
+return (struct jsonElement *) ele;
+}
+
+static struct jsonElement *jsonParseNumber(char *str, int *posPtr)
+{
+int i;
+for(i = 0;; i++)
+    {
+    char c = str[*posPtr + i];
+    if(!(c && (isdigit(c) || c == '.' || c == '-' || c == '+')))
+        break;
+    }
+char *val = cloneStringZ(str + *posPtr, i);
+*posPtr += i;
+if(strchr(val, '.') == NULL)
+    return (struct jsonElement *) newJsonNumber(sqlLongLong(val));
+else
+    return (struct jsonElement *) newJsonDouble(sqlDouble(val));
+}
+
+static struct jsonElement *jsonParseExpression(char *str, int *posPtr)
+{
+skipLeadingSpacesWithPos(str, posPtr);
+char c = str[*posPtr];
+if(c == '{')
+    return jsonParseObject(str, posPtr);
+else if (c == '[')
+    return jsonParseList(str, posPtr);
+else if (c == '"')
+    return jsonParseString(str, posPtr);
+else if (isdigit(c) || c == '-')
+    return jsonParseNumber(str, posPtr);
+else
+    return jsonParseBoolean(str, posPtr);
+// XXXX support null?
+}
+
+struct jsonElement *jsonParse(char *str)
+{
+// parse string into an in-memory json representation
+int pos = 0;
+struct jsonElement *ele = jsonParseExpression(str, &pos);
+skipLeadingSpacesWithPos(str, &pos);
+if(str[pos])
+    errAbort("Invalid JSON: unprocessed trailing string at position: %d: %s", pos, str + pos);
+return ele;
+}