a4f5eb337ffaa1e4e9eaf9b45aa3543932236906
kent
  Wed Aug 7 17:13:35 2019 -0700
Added in parameter type and count checks to built in functions.  Added three new functions: now(), md5(), and split()

diff --git src/hg/oneShot/freen/freen.c src/hg/oneShot/freen/freen.c
index eb0a727..ce09600 100644
--- src/hg/oneShot/freen/freen.c
+++ src/hg/oneShot/freen/freen.c
@@ -4,30 +4,31 @@
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "linefile.h"
 #include "hash.h"
 #include "options.h"
 #include "dystring.h"
 #include "cheapcgi.h"
 #include "jksql.h"
 #include "portable.h"
 #include "obscure.h"
 #include "localmem.h"
 #include "csv.h"
 #include "tokenizer.h"
 #include "strex.h"
+#include "hmac.h"
 
 /* Command line validation table. */
 static struct optionSpec options[] = {
    {NULL, 0},
 };
 
 void usage()
 {
 errAbort("freen - test some hairbrained thing.\n"
          "usage:  freen input\n");
 }
 
 /*  strex -String Expression - stuff to implement a relatively simple string
  *  processing expression parser. */
 
@@ -35,30 +36,33 @@
 enum strexType
 /* A type */
     {
     strexTypeBoolean = 1,
     strexTypeString = 2,
     strexTypeInt = 3,
     strexTypeDouble = 4,
     };
 
 enum strexBuiltInFunc
 /* One of these for each builtIn.  We'll just do a switch to implement */
     {
     strexBuiltInTrim,
     strexBuiltInBetween,
     strexBuiltInSpaced,
+    strexBuiltInNow,
+    strexBuiltInMd5,
+    strexBuiltInSplit,
     };
 
 struct strexBuiltIn
 /* A built in function */
     {
     char *name;
     enum strexBuiltInFunc func;
     int paramCount;
     enum strexType *paramTypes;
     };
 
 
 union strexVal
 /* Some value of arbirary type that can be of any type corresponding to strexType */
     {
@@ -117,35 +121,39 @@
     enum strexType type;		/* Return type of this operation. */
     union strexVal val;		/* Return value of this operation. */
     };
 
 struct strexIn
 /* Input to the strex parser */
     {
     struct tokenizer *tkz;  /* Get next text input from here */
     struct hash *builtInHash;  /* Hash of built in functions */
     };
 
 enum strexType oneString[] = {strexTypeString};
 enum strexType twoStrings[] = {strexTypeString, strexTypeString};
 enum strexType threeStrings[] = {strexTypeString, strexTypeString, strexTypeString};
 enum strexType stringInt[] = {strexTypeString, strexTypeInt};
+enum strexType stringStringInt[] = {strexTypeString, strexTypeString, strexTypeInt};
 
 static struct strexBuiltIn builtins[] = {
     { "trim", strexBuiltInTrim, 1, oneString, },
     { "between", strexBuiltInBetween, 3, threeStrings, },
-    { "spaced", strexBuiltInSpaced, 3, stringInt },
+    { "spaced", strexBuiltInSpaced, 2, stringInt },
+    { "now", strexBuiltInNow, 0, NULL },
+    { "md5", strexBuiltInMd5, 1, oneString },
+    { "split", strexBuiltInSplit, 3, stringStringInt },
 };
 
 
 
 static struct hash *hashBuiltIns()
 /* Build a hash of builtins keyed by name */
 {
 struct hash *hash = hashNew(0);
 int i;
 for (i=0; i<ArraySize(builtins); ++i)
     hashAdd(hash, builtins[i].name, &builtins[i]);
 return hash;
 }
 
 static struct strexIn *strexInNew(char *expression)
@@ -181,30 +189,53 @@
     case strexTypeBoolean:
         fprintf(f, "%s", (val.b ? "true" : "false") );
 	break;
     case strexTypeString:
         fprintf(f, "%s", (val.s == NULL ? "(null)" : val.s));
 	break;
     case strexTypeInt:
         fprintf(f, "%lld", val.i);
 	break;
     case strexTypeDouble:
         fprintf(f, "%f", val.x);
 	break;
     }
 }
 
+static char *strexTypeToString(enum strexType type)
+/* Return a string representation of type */
+{
+switch (type)
+    {
+    case strexTypeBoolean:
+	return "boolean";
+	break;
+    case strexTypeString:
+	return "string";
+	break;
+    case strexTypeInt:
+	return "integer";
+	break;
+    case strexTypeDouble:
+	return "floating point";
+	break;
+    default:
+        internalErr();
+	return NULL;
+    }
+}
+
 static char *strexOpToString(enum strexOp op)
 /* Return string representation of parse op. */
 {
 switch (op)
     {
     case strexOpLiteral:
 	return "strexOpLiteral";
     case strexOpSymbol:
 	return "strexOpSymbol";
     
     case strexOpStringToBoolean:
         return "strexOpStringToBoolean";
     case strexOpIntToBoolean:
         return "strexOpIntToBoolean";
     case strexOpDoubleToBoolean:
@@ -507,30 +538,49 @@
         {
 	tokenizerReuse(tkz);
 	for (;;)
 	    {
 	    struct strexParse *param = strexParseExpression(in);
 	    slAddHead(&function->children, param);
 	    tok = tokenizerMustHaveNext(tkz);
 	    if (tok[0] == ')')
 	        break;
 	    else if (tok[0] != ',')
 	        errAbort("Error in parameter list for %s line %d of %s", function->val.s, 
 		    tkz->lf->lineIx, tkz->lf->fileName);
 	    }
 	slReverse(&function->children);
 	}
+
+    /* Check function parameter count */
+    int childCount = slCount(function->children);
+    if (childCount != builtIn->paramCount)
+        errAbort("Function %s has %d parameters but needs %d line %d of %s",
+	    builtIn->name, childCount, builtIn->paramCount, tkz->lf->lineIx, tkz->lf->fileName);
+	    
+    /* Check function parameter types */
+    int i;
+    struct strexParse *p;
+    for (i=0, p=function->children; i<childCount; ++i, p = p->next)
+        {
+	if (p->type != builtIn->paramTypes[i])
+	    {
+	    errAbort("Parameter #%d to %s needs to be type %s not %s line %d of %s",
+		i, builtIn->name,  strexTypeToString(builtIn->paramTypes[i]), 
+		strexTypeToString(p->type), tkz->lf->lineIx, tkz->lf->fileName);
+	    }
+	}
     }
 else
     tokenizerReuse(tkz);
 return function;
 }
 
 static struct strexParse *strexParseIndex(struct strexIn *in)
 /* Handle the [] in this[6].  Convert it into tree:
 *         strexOpArrayIx
 *            strexParseFunction
 *            strexParseFunction */
 {
 struct tokenizer *tkz = in->tkz;
 struct strexParse *collection = strexParseFunction(in);
 struct strexParse *p = collection;
@@ -722,82 +772,122 @@
 	struct lm *lm)
 /* Handle parse tree generated by an indexed array. */
 {
 struct strexParse *array = p->children;
 struct strexParse *index = array->next;
 struct strexEval arrayVal = strexLocalEval(array, record, lookup, lm);
 struct strexEval indexVal = strexLocalEval(index, record, lookup, lm);
 struct strexEval res;
 struct dyString *scratch = dyStringNew(0);
 char *val = emptyForNull(csvParseOneOut(arrayVal.val.s, indexVal.val.i, scratch));
 res.val.s = cloneString(val);
 res.type = strexTypeString;
 return res;
 }
 
-char *wordInString(char *words,  int ix,  struct lm *lm)
+static char *wordInString(char *words,  int ix,  struct lm *lm)
 /* Return the word delimited string of index ix as clone into lm */
 {
 char *s = words;
 int i;
 for (i=0; ; ++i)
     {
     s = skipLeadingSpaces(s);
     if (isEmpty(s))
         errAbort("There aren't %d words in %s", ix+1, words);
     char *end = skipToSpaces(s);
     if (i == ix)
         {
 	if (end == NULL)
 	    return lmCloneString(lm, s);
 	else
 	    return lmCloneMem(lm, s, end - s);
 	}
     s = end;
     }
 }
 
+static char *splitString(char *string, char *splitter, int ix, struct lm *lm)
+/* Return the ix'th part of string as split apart by splitter */
+{
+int splitterSize = strlen(splitter);
+if (splitterSize != 1)
+    errAbort("Separator parameter to split must be a single character, not %s", splitter);
+int count = chopByChar(string, splitter[0], NULL, 0);
+if (ix >= count)
+    errAbort("There aren't %d fields separated by %s in %s", ix+1, splitter, string);
+char **row;
+lmAllocArray(lm, row, count);
+char *scratch = lmCloneString(lm, string);
+chopByChar(scratch, splitter[0], row, count);
+return row[ix];
+}
+
 static struct strexEval strexEvalCallBuiltIn(struct strexParse *p, 
     void *record, StrexEvalLookup lookup, struct lm *lm)
 /* Handle parse tree generated by an indexed array. */
 {
 struct strexBuiltIn *builtIn = p->val.builtIn;
 struct strexEval res;
 res.type = strexTypeString;
 switch (builtIn->func)
     {
     case strexBuiltInTrim:
 	{
         struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
 	res.val.s = trimSpaces(a.val.s);
 	break;
 	}
     case strexBuiltInBetween:
 	{
         struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
         struct strexEval b = strexLocalEval(p->children->next, record, lookup, lm);
         struct strexEval c = strexLocalEval(p->children->next->next, record, lookup, lm);
 	char *between = stringBetween(a.val.s, c.val.s, b.val.s);
 	res.val.s = lmCloneString(lm, between);
 	freeMem(between);
         break;
 	}
     case strexBuiltInSpaced:
         {
         struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
         struct strexEval b = strexLocalEval(p->children->next, record, lookup, lm);
 	res.val.s = wordInString(a.val.s, b.val.i, lm);
+	break;
+	}
+    case strexBuiltInNow:
+        {
+	time_t now = time(NULL);
+	res.val.s = lmCloneString(lm, ctime(&now));
+	eraseTrailingSpaces(res.val.s);
+	break;
+	}
+    case strexBuiltInMd5:
+        {
+        struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
+	char *md5 = hmacMd5("", a.val.s);
+	res.val.s = lmCloneString(lm, md5);
+	freez(&md5);
+	break;
+	}
+    case strexBuiltInSplit:
+        {
+        struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
+        struct strexEval b = strexLocalEval(p->children->next, record, lookup, lm);
+        struct strexEval c = strexLocalEval(p->children->next->next, record, lookup, lm);
+	res.val.s = splitString(a.val.s, b.val.s, c.val.i, lm);
+	break;
 	}
     }
 return res;
 }
 
 
 /* ~~~ evaluation part of strex */
 
 static struct strexEval strexLocalEval(struct strexParse *p, void *record, StrexEvalLookup lookup, 
 	struct lm *lm)
 /* Evaluate self on parse tree, allocating memory if needed from lm. */
 {
 struct strexEval res;
 switch (p->op)
     {