5ec42ac16ff4d62a5ca390ba108071d3e4ea53e3 kent Thu Dec 9 12:58:05 2021 -0800 Supporting comparison operations. diff --git src/lib/strex.c src/lib/strex.c index 7c4efa1..7100492 100644 --- src/lib/strex.c +++ src/lib/strex.c @@ -112,30 +112,36 @@ 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, /* Binary operations. */ strexOpAdd, strexOpSubtract, + strexOpGt, + strexOpLt, + strexOpGe, + strexOpLe, + strexOpEq, + strexOpNe, strexOpOr, strexOpAnd, /* Type conversions - possibly a few more than we actually need at the moment. */ strexOpStringToBoolean, strexOpIntToBoolean, strexOpDoubleToBoolean, strexOpStringToInt, strexOpDoubleToInt, strexOpBooleanToInt, strexOpStringToDouble, strexOpBooleanToDouble, strexOpIntToDouble, strexOpBooleanToString, strexOpIntToString, @@ -206,30 +212,31 @@ /* 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(struct lineFile *lf, void *symbols, StrexLookup lookup) /* Return a new strexIn structure wrapped around lineFile */ { struct tokenizer *tkz = tokenizerOnLineFile(lf); tkz->leaveQuotes = TRUE; +tkz->twoCharOps = TRUE; struct strexIn *si; AllocVar(si); si->tkz = tkz; si->builtInHash = hashBuiltIns(); si->importHash = hashNew(4); si->symbols = symbols; si->lookup = lookup; return si; } static void strexInFree(struct strexIn **pSi) /* Free up memory associated with strexIn structure */ { struct strexIn *si = *pSi; if (si != NULL) @@ -332,33 +339,47 @@ return "strexOpBooleanToDouble"; case strexOpIntToDouble: return "strexOpIntToDouble"; case strexOpBooleanToString: return "strexOpBooleanToString"; case strexOpIntToString: return "strexOpIntToString"; case strexOpDoubleToString: return "strexOpDoubleToString"; case strexOpUnaryMinusInt: return "strexOpUnaryMinusInt"; case strexOpUnaryMinusDouble: return "strexOpUnaryMinusDouble"; - case strexOpAdd: return "strexOpAdd"; + case strexOpSubtract: + return "strexOpSubtract"; + case strexOpEq: + return "strexOpEq"; + case strexOpNe: + return "strexOpNe"; + case strexOpGt: + return "strexOpGt"; + case strexOpLt: + return "strexOpLt"; + case strexOpGe: + return "strexOpGe"; + case strexOpLe: + return "strexOpLe"; + case strexOpOr: return "strexOpOr"; case strexOpAnd: return "strexOpAnd"; case strexOpBuiltInCall: return "strexOpBuiltInCall"; case strexOpPick: return "strexOpPick"; case strexOpConditional: return "strexOpConditional"; case strexOpArrayIx: return "strexOpArrayIx"; case strexOpArrayRange: @@ -1007,47 +1028,126 @@ } l = strexParseCoerce(l, childType); r = strexParseCoerce(r, childType); /* Create the binary operation */ AllocVar(p); p->op = strexOpAdd; p->type = childType; /* Now hang children onto node. */ p->children = l; l->next = r; } } +boolean isCmpOp(char *tok) +/* Return TRUE if tok is one of t > < >= <= = and !=. */ +{ +char t0 = tok[0]; +char t1 = tok[1]; +switch (t0) + { + case '>': + case '<': + return t1 == 0 || t1 == '='; + case '=': + return t1 == 0; + case '!': + return t1 == '='; + default: + return FALSE; + } +} + +enum strexOp strexOpForCmpTok(char *tok) +/* Return TRUE if tok is one of t > < >= <= = and !=. */ +{ +if (sameString(tok, "=")) + return strexOpEq; +else if (sameString(tok, "!=")) + return strexOpNe; +else if (sameString(tok, ">")) + return strexOpGt; +else if (sameString(tok, ">=")) + return strexOpGe; +else if (sameString(tok, "<")) + return strexOpLt; +else if (sameString(tok, "<=")) + return strexOpLe; +else + { + warn("unrecognized cmp op token %s", tok); + internalErr(); + return strexOpUnknown; + } +} + + +static struct strexParse *strexParseCompareOp(struct strexIn *in) +/* Parse out > < >= <= = and !=. */ +{ +struct tokenizer *tkz = in->tkz; +struct strexParse *p = strexParseSum(in); +char *tok = tokenizerNext(tkz); +if (tok == NULL || !isCmpOp(tok)) + { + tokenizerReuse(tkz); + return p; + } +enum strexOp cmpOp = strexOpForCmpTok(tok); + +/* What we've parsed so far becomes left side of binary op, next term ends up on right. */ +struct strexParse *l = p; +struct strexParse *r = strexParseSum(in); + +/* Make left and right side into a common type */ +enum strexType commonType = commonTypeForMathBop(l->type, r->type); +if (commonType == strexTypeBoolean) + commonType = strexTypeInt; + +l = strexParseCoerce(l, commonType); +r = strexParseCoerce(r, commonType); + +/* Create the binary operation */ +AllocVar(p); +p->op = cmpOp; +p->type = strexTypeBoolean; + +/* Now hang children onto node and return it. */ +p->children = l; +l->next = r; +return p; +} + static struct strexParse *strexParseAnd(struct strexIn *in) /* Parse out plus or minus. */ { struct tokenizer *tkz = in->tkz; -struct strexParse *p = strexParseSum(in); +struct strexParse *p = strexParseCompareOp(in); for (;;) { char *tok = tokenizerNext(tkz); if (tok == NULL || differentString(tok, "and")) { tokenizerReuse(tkz); return p; } /* What we've parsed so far becomes left side of binary op, next term ends up on right. */ struct strexParse *l = p; - struct strexParse *r = strexParseSum(in); + struct strexParse *r = strexParseCompareOp(in); /* Make left and right side into a common type */ enum strexType childType = commonTypeForLogicBop(l->type, r->type); l = strexParseCoerce(l, childType); r = strexParseCoerce(r, childType); /* Create the binary operation */ AllocVar(p); p->op = strexOpAnd; p->type = childType; /* Now hang children onto node. */ p->children = l; l->next = r; } @@ -1299,30 +1399,217 @@ char *s = lmAlloc(run->lm, lLen + rLen + 1); memcpy(s, lv.val.s, lLen); memcpy(s+lLen, rv.val.s, rLen); res.val.s = s; break; } default: internalErr(); res.val.b = FALSE; break; } res.type = lv.type; return res; } +static struct strexEval strexEvalEq(struct strexParse *p, struct strexRun *run) +/* Return a == b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i == rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x == rv.val.x); + break; + case strexTypeString: + { + res.val.b = (strcasecmp(lv.val.s, rv.val.s) == 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + +static struct strexEval strexEvalNe(struct strexParse *p, struct strexRun *run) +/* Return a != b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i != rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x != rv.val.x); + break; + case strexTypeString: + { + res.val.b = (strcasecmp(lv.val.s, rv.val.s) != 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + + +static struct strexEval strexEvalGt(struct strexParse *p, struct strexRun *run) +/* Return a > b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i > rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x > rv.val.x); + break; + case strexTypeString: + { + res.val.b = (cmpWordsWithEmbeddedNumbers(lv.val.s, rv.val.s) > 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + +static struct strexEval strexEvalLt(struct strexParse *p, struct strexRun *run) +/* Return a < b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i < rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x < rv.val.x); + break; + case strexTypeString: + { + res.val.b = (cmpWordsWithEmbeddedNumbers(lv.val.s, rv.val.s) < 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + +static struct strexEval strexEvalGe(struct strexParse *p, struct strexRun *run) +/* Return a > b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i >= rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x >= rv.val.x); + break; + case strexTypeString: + { + res.val.b = (cmpWordsWithEmbeddedNumbers(lv.val.s, rv.val.s) >= 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + +static struct strexEval strexEvalLe(struct strexParse *p, struct strexRun *run) +/* Return a < b. */ +{ +struct strexParse *lp = p->children; +struct strexParse *rp = lp->next; +struct strexEval lv = strexLocalEval(lp, run); +struct strexEval rv = strexLocalEval(rp, run); +struct strexEval res; +assert(lv.type == rv.type); // Is our type automatic casting working? +switch (lv.type) + { + case strexTypeInt: + res.val.b = (lv.val.i <= rv.val.i); + break; + case strexTypeDouble: + res.val.b = (lv.val.x <= rv.val.x); + break; + case strexTypeString: + { + res.val.b = (cmpWordsWithEmbeddedNumbers(lv.val.s, rv.val.s) <= 0); + break; + } + default: + internalErr(); + res.val.b = FALSE; + break; + } +res.type = strexTypeBoolean; +return res; +} + static struct strexEval strexEvalOr(struct strexParse *p, struct strexRun *run) /* Return a or b. */ { struct strexParse *lp = p->children; struct strexParse *rp = lp->next; struct strexEval res = strexLocalEval(lp, run); boolean gotIt = TRUE; switch (res.type) { case strexTypeBoolean: gotIt = res.val.b != 0; break; case strexTypeInt: gotIt = (res.val.i != 0); @@ -2088,30 +2375,50 @@ res = strexEvalPick(p, run); break; case strexOpConditional: res = strexEvalConditional(p, run); break; /* Mathematical ops, simple binary type */ case strexOpAdd: res = strexEvalAdd(p, run); break; case strexOpSubtract: res = strexEvalSubtract(p, run); break; + /* Comparison ops, simple binary type */ + case strexOpEq: + res = strexEvalEq(p, run); + break; + case strexOpNe: + res = strexEvalNe(p, run); + break; + case strexOpGt: + res = strexEvalGt(p, run); + break; + case strexOpLt: + res = strexEvalLt(p, run); + break; + case strexOpGe: + res = strexEvalGe(p, run); + break; + case strexOpLe: + res = strexEvalLe(p, run); + break; + /* Logical ops, simple binary type and not*/ case strexOpOr: res = strexEvalOr(p, run); break; case strexOpAnd: res = strexEvalAnd(p, run); break; case strexOpNot: res = strexLocalEval(p->children, run); res.val.b = !res.val.b; break; default: errAbort("Unknown op %s\n", strexOpToString(p->op)); res.type = strexTypeInt; // Keep compiler from complaining.