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; ichildren, 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; inext) + { + 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) {