0019ae3b59457f94944a36cfa4bdebc72b017dba
kent
  Sun Aug 11 20:36:23 2019 -0700
Adding new strip function.  Making replace handle empty oldval in a useful way, as a way for setting defaults when a value is empty.

diff --git src/lib/strex.c src/lib/strex.c
index eecb5b1..c470a7a 100644
--- src/lib/strex.c
+++ src/lib/strex.c
@@ -45,30 +45,31 @@
 
 enum strexBuiltInFunc
 /* One of these for each builtIn.  We'll just do a switch to implement 
  * Each built in function needs a value here, to keep it simple there's
  * aa correspondence between these names and the built in function name */
     {
     strexBuiltInTrim,
     strexBuiltInBetween,
     strexBuiltInSplit,
     strexBuiltInNow,
     strexBuiltInMd5,
     strexBuiltInSeparate,
     strexBuiltInUncsv,
     strexBuiltInUntsv,
     strexBuiltInReplace,
+    strexBuiltInStrip,
     };
 
 struct strexBuiltIn
 /* Information to describe a built in function */
     {
     char *name;		/* Name in strex language:  trim, split, etc */
     enum strexBuiltInFunc func;  /* enum version: strexBuiltInTrim strexBuiltInSplit etc. */
     int paramCount;	/* Number of parameters, not flexible in this language! */
     enum strexType *paramTypes;  /* Array of types, one for each parameter */
     };
 
 union strexVal
 /* Some value of arbirary type that can be of any type corresponding to strexType */
     {
     boolean b;
@@ -128,47 +129,48 @@
     struct strexParse *children;	/* Points to oldest child if any. */
     enum strexOp op;		/* Operation at this node. */
     enum strexType type;		/* Return type of this operation. */
     union strexVal val;		/* Return value of this operation. */
     };
 
 struct strexIn
 /* Input to the strex parser - tokenizer and a hash full of built in functions. */
     {
     struct tokenizer *tkz;  /* Get next text input from here */
     struct hash *builtInHash;  /* Hash of built in functions */
     };
 
 /* Some predefined lists of parameter types */
 static enum strexType oneString[] = {strexTypeString};
-// static enum strexType twoStrings[] = {strexTypeString, strexTypeString};
+static enum strexType twoStrings[] = {strexTypeString, strexTypeString};
 static enum strexType threeStrings[] = {strexTypeString, strexTypeString, strexTypeString};
 static enum strexType stringInt[] = {strexTypeString, strexTypeInt};
 static enum strexType stringStringInt[] = {strexTypeString, strexTypeString, strexTypeInt};
 
 /* There's one element here for each built in function.  There's also a few switches you'll need to
  * fill in if you add a new built in function. */
 static struct strexBuiltIn builtins[] = {
     { "trim", strexBuiltInTrim, 1, oneString, },
     { "between", strexBuiltInBetween, 3, threeStrings },
     { "split", strexBuiltInSplit, 2, stringInt },
     { "now", strexBuiltInNow, 0, NULL },
     { "md5", strexBuiltInMd5, 1, oneString },
     { "separate", strexBuiltInSeparate, 3, stringStringInt },
     { "uncsv", strexBuiltInUncsv, 2, stringInt },
     { "untsv", strexBuiltInUntsv, 2, stringInt },
     { "replace", strexBuiltInReplace, 3, threeStrings },
+    { "strip", strexBuiltInStrip, 2, twoStrings },
 };
 
 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, char *fileName, int fileLineNumber)
 /* Return a new strexIn structure wrapped around expression */
 {
@@ -958,35 +960,56 @@
 lmAllocArray(lm, row, count);
 char *scratch = lmCloneString(lm, string);
 chopByChar(scratch, splitter[0], row, count);
 return row[ix];
 }
 
 static char *untsvString(char *tsvIn, int ix, struct lm *lm)
 /* Return the tab separated value at given index living somewhere in lm. */
 {
 return separateString(tsvIn, "\t", ix, lm);
 }
 
 static char *replaceString(char *in, char *oldVal, char *newVal, struct lm *lm)
 /* Replace every occurrence of oldVal with newVal in string */
 {
+if (sameString(in, oldVal) )
+    return lmCloneString(lm, newVal);  // Simple case that also handles empty oldVal match empty in
+else
+    {
+    if (isEmpty(oldVal))
+        return lmCloneString(lm, in);
+    else
+	{
 	char *s = replaceChars(in, oldVal, newVal);
 	char *result = lmCloneString(lm, s);  // Move to local memory
 	freeMem(s);
 	return result;
 	}
+    }
+}
+
+static char *stripAll(char *in, char *toRemove, struct lm *lm)
+/* Remove every occurrence of any of the chars in toRemove from in. */
+{
+char *result = lmCloneString(lm, in);  // Move to local memory
+char c, *s = toRemove;
+while ((c = *s++) != 0)
+    stripChar(result, c);
+return result;
+}
+
 
 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;
@@ -1041,30 +1064,37 @@
     case strexBuiltInUntsv:
         {
         struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
         struct strexEval b = strexLocalEval(p->children->next, record, lookup, lm);
 	res.val.s = untsvString(a.val.s, b.val.i, lm);
 	break;
 	}
     case strexBuiltInReplace:
         {
         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 = replaceString(a.val.s, b.val.s, c.val.s, lm);
 	break;
 	}
+    case strexBuiltInStrip:
+        {
+        struct strexEval a = strexLocalEval(p->children, record, lookup, lm);
+        struct strexEval b = strexLocalEval(p->children->next, record, lookup, lm);
+	res.val.s = stripAll(a.val.s, b.val.s, lm);
+	break;
+	}
     }
 return res;
 }
 
 
 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)
     {
     case strexOpLiteral:
 	res.val = p->val;
 	res.type = p->type;