835a903827681f7295594708dccaec0cbf2e20fa kent Mon Aug 12 21:37:48 2019 -0700 Adding new built-in function fix() for doing constant/constant substitutions. Allowing negative indexes in split() and separate() that work like negative array indexes do. diff --git src/lib/strex.c src/lib/strex.c index c470a7a..51d5e55 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, + strexBuiltInFix, 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 */ { @@ -146,30 +147,31 @@ 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 }, + { "fix", strexBuiltInFix, 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 */ @@ -906,68 +908,81 @@ end = strlen(arraySource) + end; if (start < 0) start = 0; if (end > len) end = len; if (end < start) end = start; // errors apparently just get truncated in this language. hmm struct strexEval res; res.val.s = lmCloneStringZ(lm, arrayVal.val.s + start, end-start); res.type = strexTypeString; return res; } static char *splitString(char *words, int ix, struct lm *lm) /* Return the space-delimited word of index ix as clone into lm */ { +if (ix < 0) // Negative index. We got to count, dang + { + int wordCount = chopByWhite(words, NULL, 0); + ix = wordCount + ix; + if (ix < 0) + return ""; + } 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); + return ""; 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 *uncsvString(char *csvIn, int ix, struct lm *lm) /* Return the comma separated value of index ix. Memory for result is lm */ { struct dyString *scratch = dyStringNew(0); char *one = csvParseOneOut(csvIn, ix, scratch); char *res = emptyForNull(lmCloneString(lm, one)); // Save in more permanent memory dyStringFree(&scratch); return res; } static char *separateString(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 < 0) // Negative index. No problem, count from the end + { + ix = count + ix; + if (ix < 0) + return ""; // Still out of range, oh well + } if (ix >= count) - errAbort("There aren't %d fields separated by %s in %s", ix+1, splitter, string); + return ""; char **row; 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 */ @@ -1064,30 +1079,43 @@ 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 strexBuiltInFix: + { + struct strexEval string = strexLocalEval(p->children, record, lookup, lm); + struct strexEval oldVal = strexLocalEval(p->children->next, record, lookup, lm); + struct strexEval newVal = strexLocalEval(p->children->next->next, record, lookup, lm); + if (sameString(string.val.s, oldVal.val.s)) + { + res.val.s = newVal.val.s; + } + else + res.val.s = string.val.s; + 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. */