2be851c8acf32ce69189a0d0bae0d81d9523ac5f
kent
  Thu Dec 9 16:06:58 2021 -0800
Adding new symbol_id() builtin function.

diff --git src/lib/strex.c src/lib/strex.c
index 7100492..2736f3d 100644
--- src/lib/strex.c
+++ src/lib/strex.c
@@ -47,30 +47,31 @@
  * aa correspondence between these names and the built in function name */
     {
     strexBuiltInTrim,
     strexBuiltInBetween,
     strexBuiltInWord,
     strexBuiltInNow,
     strexBuiltInMd5,
     strexBuiltInChop,
     strexBuiltInUncsv,
     strexBuiltInUntsv,
     strexBuiltInReplace,
     strexBuiltInFix,
     strexBuiltInStrip,
     strexBuiltInLen,
     strexBuiltInSymbol,
+    strexBuiltInSymbolId,
     strexBuiltInLower,
     strexBuiltInUpper,
     strexBuiltInIn, 
     strexBuiltInStarts,
     strexBuiltInEnds,
     strexBuiltInSame,
     strexBuiltInTidy,
     strexBuiltInWarn,
     strexBuiltInError,
     strexBuiltInLetterRange,
     strexBuiltInWordRange,
     strexBuiltInChopRange,
     };
 
 struct strexBuiltIn
@@ -93,31 +94,31 @@
     struct strexBuiltIn *builtIn;
     };
 
 struct strexEval
 /* Result of evaluation of parse tree. */
     {
     enum strexType type;
     union strexVal val;
     };
 
 enum strexOp
 /* An operation in the parse tree. */
     {
     strexOpUnknown,	/* Should not occur */
     strexOpLiteral,        /* Literal string or number. */
-    strexOpSymbol,	/* A symbol name. */
+    strexOpSymbol,	/* Symbol/variable. */
 
     strexOpBuiltInCall,	/* Call a built in function */
     strexOpPick,	/* Similar to built in but pick deserves it's own op. */
     strexOpConditional,	/* Conditional trinary operation */
 
     strexOpArrayIx,	/* An array with an index. */
     strexOpArrayRange,	/* An array with a range. */
 
     strexOpStrlen,	/* Length of a string */
 
     /* Unary minus for numbers, logical not */
     strexOpUnaryMinusInt,
     strexOpUnaryMinusDouble,
     strexOpNot,
 
@@ -182,30 +183,31 @@
  * fill in if you add a new built in function. */
 static struct strexBuiltIn builtins[] = {
     { "trim", strexBuiltInTrim, strexTypeString, 1, oneString, },
     { "between", strexBuiltInBetween, strexTypeString, 3, threeStrings },
     { "word", strexBuiltInWord, strexTypeString, 2, stringInt },
     { "now", strexBuiltInNow, strexTypeString, 0, NULL },
     { "md5", strexBuiltInMd5, strexTypeString, 1, oneString },
     { "chop", strexBuiltInChop, strexTypeString, 3, stringStringInt },
     { "uncsv", strexBuiltInUncsv, strexTypeString, 2, stringInt },
     { "untsv", strexBuiltInUntsv, strexTypeString, 2, stringInt },
     { "replace", strexBuiltInReplace, strexTypeString, 3, threeStrings },
     { "fix", strexBuiltInFix, strexTypeString, 3, threeStrings },
     { "strip", strexBuiltInStrip, strexTypeString, 2, twoStrings },
     { "len", strexBuiltInLen, strexTypeInt, 1, oneString},
     { "symbol", strexBuiltInSymbol, strexTypeString, 2, twoStrings },
+    { "symbol_id", strexBuiltInSymbolId, strexTypeString, 2, twoStrings },
     { "upper", strexBuiltInUpper, strexTypeString, 1, oneString },
     { "lower", strexBuiltInLower, strexTypeString, 1, oneString },
     { "in", strexBuiltInIn, strexTypeBoolean, 2, twoStrings },
     { "starts_with", strexBuiltInStarts, strexTypeBoolean, 2, twoStrings}, 
     { "ends_with", strexBuiltInEnds, strexTypeBoolean, 2, twoStrings}, 
     { "same", strexBuiltInSame, strexTypeBoolean, 2, twoStrings}, 
     { "tidy", strexBuiltInTidy, strexTypeString, 3, threeStrings },
     { "warn", strexBuiltInWarn, strexTypeString, 1, oneString},
     { "error", strexBuiltInError, strexTypeString, 1, oneString},
     { "letter_range", strexBuiltInLetterRange, strexTypeString, 3, stringIntInt},
     { "word_range", strexBuiltInWordRange, strexTypeString, 3, stringIntInt},
     { "chop_range", strexBuiltInChopRange, strexTypeString, 4, stringStringIntInt},
 };
 
 static struct hash *hashBuiltIns()
@@ -1939,30 +1941,54 @@
 	 out += strlen(hexBuf);
 	 }
      }
 *out++ = 0;
 int len = strlen(result) - prefixSize;
 if (len > 32)
     {
     char *md5 = hmacMd5("", original);
     strcpy(result + prefixSize, md5);
     freeMem(md5);
     }
 
 return result;
 }
 
+static char *idify(char *prefix, char *original, struct lm *lm)
+/* Convert original to something could use as a prefix plus a numeric id */
+{
+static struct hash *prefixHash = NULL;
+if (prefixHash == NULL)
+    prefixHash = hashNew(0);
+struct hash *idHash = hashFindVal(prefixHash, prefix);
+if (idHash == NULL)
+    {
+    idHash = hashNew(0);
+    hashAdd(prefixHash, prefix, idHash);
+    }
+char *id = hashFindVal(idHash, original);
+if (id == NULL)
+    {
+    char symbuf[128];
+    safef(symbuf, sizeof(symbuf), "%s%d", prefix, idHash->elCount + 1);
+    id = lmCloneString(idHash->lm, symbuf);
+    hashAdd(idHash, original, id);
+    }
+return id;
+}
+
+
 static struct strexEval strexEvalCallBuiltIn(struct strexParse *p, struct strexRun *run)
 /* Handle parse tree generated by call to a built in function. */
 {
 struct strexBuiltIn *builtIn = p->val.builtIn;
 struct strexEval res;
 res.type = builtIn->returnType;
 struct lm *lm = run->lm;
 
 switch (builtIn->func)
     {
     case strexBuiltInTrim:
 	{
         struct strexEval a = strexLocalEval(p->children, run);
 	res.val.s = trimSpaces(a.val.s);
 	break;
@@ -2054,30 +2080,37 @@
 	break;
 	}
     case strexBuiltInLen:
         {
         struct strexEval a = strexLocalEval(p->children, run);
 	res.val.i = strlen(a.val.s);
 	break;
 	}
     case strexBuiltInSymbol:  // Convert string to something could use as a C language symbol
         {
         struct strexEval a = strexLocalEval(p->children, run);
         struct strexEval b = strexLocalEval(p->children->next, run);
 	res.val.s = symbolify(a.val.s, b.val.s, lm);
 	break;
 	}
+    case strexBuiltInSymbolId:  // Convert string to something could use numeric id
+        {
+        struct strexEval a = strexLocalEval(p->children, run);
+        struct strexEval b = strexLocalEval(p->children->next, run);
+	res.val.s = idify(a.val.s, b.val.s, lm);
+	break;
+	}
     case strexBuiltInLower:
         {
         struct strexEval a = strexLocalEval(p->children, run);
 	res.val.s = lmCloneString(lm, a.val.s);
 	tolowers(res.val.s);
 	break;
 	}
     case strexBuiltInUpper:
         {
         struct strexEval a = strexLocalEval(p->children, run);
 	res.val.s = lmCloneString(lm, a.val.s);
 	touppers(res.val.s);
 	break;
 	}
     case strexBuiltInIn: