99c8ed08eb3655325a40db805e9ba44db0302bd6 kent Fri Aug 9 23:39:05 2019 -0700 Fixed off-by-one problem on empty end ranges. diff --git src/lib/strex.c src/lib/strex.c index 3d20516..16d8de2 100644 --- src/lib/strex.c +++ src/lib/strex.c @@ -625,67 +625,64 @@ if (tok == NULL) tokenizerReuse(tkz); else if (tok[0] == '[') { array = strexParseCoerce(array, strexTypeString); tok = tokenizerMustHaveNext(tkz); if (tok[0] == ':') // Case where is a range with empty beginning. We can even compute index. { tok = tokenizerMustHaveNext(tkz); if (tok[0] == ']') { tokenizerReuse(tkz); // Range is just whole array, do nothing really } else { - tokenizerReuse(tkz); // Range is just whole array, do nothing really + tokenizerReuse(tkz); struct strexParse *firstIndex = strexParseNew(strexOpLiteral, strexTypeInt); struct strexParse *secondIndex = strexParseCoerce(strexParseExpression(in), strexTypeInt); p = arrayRangeTree(array, firstIndex, secondIndex); } } else { tokenizerReuse(tkz); struct strexParse *firstIndex = strexParseCoerce(strexParseExpression(in), strexTypeInt); tok = tokenizerMustHaveNext(tkz); if (tok[0] == ':') { struct strexParse *secondIndex; tok = tokenizerMustHaveNext(tkz); if (tok[0] == ']') // Case where second half of rang is empty { tokenizerReuse(tkz); - secondIndex = strexParseNew(strexOpLiteral, strexTypeInt); - secondIndex->val.i = -1; + secondIndex = strexParseNew(strexOpStrlen, strexTypeInt); + secondIndex->children = array; } else { tokenizerReuse(tkz); secondIndex = strexParseCoerce(strexParseExpression(in), strexTypeInt); } p = arrayRangeTree(array, firstIndex, secondIndex); } else { // Simple no range case tokenizerReuse(tkz); - AllocVar(p); - p->op = strexOpArrayIx; - p->type = strexTypeString; + p = strexParseNew(strexOpArrayIx, strexTypeString); p->children = array; - p->val.s = cloneString(""); array->next = firstIndex; } } skipOverRequired(in, "]"); } else tokenizerReuse(tkz); return p; } static struct strexParse *strexParseUnaryMinus(struct strexIn *in) /* Return unary minus sort of parse tree if there's a leading '-' */ { struct tokenizer *tkz = in->tkz; @@ -890,31 +887,31 @@ * has just been turned into two integer values. */ { struct strexParse *array = p->children; struct strexParse *index1 = array->next; struct strexParse *index2 = index1->next; struct strexEval arrayVal = strexLocalEval(array, record, lookup, lm); struct strexEval rangeStart = strexLocalEval(index1, record, lookup, lm); struct strexEval rangeEnd = strexLocalEval(index2, record, lookup, lm); char *arraySource = arrayVal.val.s; int start = rangeStart.val.i; int end = rangeEnd.val.i; int len = strlen(arraySource); if (start < 0) start = strlen(arraySource) + start; if (end < 0) - end = strlen(arraySource) + end + 1; + 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 */ { char *s = words; @@ -1138,30 +1135,35 @@ case strexOpUnaryMinusInt: res = strexLocalEval(p->children, record, lookup, lm); res.val.i = -res.val.i; break; case strexOpUnaryMinusDouble: res = strexLocalEval(p->children, record, lookup, lm); res.val.x = -res.val.x; break; case strexOpArrayIx: res = strexEvalArrayIx(p, record, lookup, lm); break; case strexOpArrayRange: res = strexEvalArrayRange(p, record, lookup, lm); break; + case strexOpStrlen: + res = strexLocalEval(p->children, record, lookup, lm); + res.type = strexTypeInt; + res.val.i = strlen(res.val.s); + break; case strexOpBuiltInCall: res = strexEvalCallBuiltIn(p, record, lookup, lm); break; /* Mathematical ops, simple binary type */ case strexOpAdd: res = strexEvalAdd(p, record, lookup, lm); break; default: errAbort("Unknown op %s\n", strexOpToString(p->op)); res.type = strexTypeInt; // Keep compiler from complaining. res.val.i = 0; // Keep compiler from complaining. break;