diff --git a/VERSION b/VERSION index 8232290..56dd60e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.176 +1.5.177 diff --git a/base/netcmp.c b/base/netcmp.c index 3a29ae4..3a99e0c 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -4058,24 +4058,33 @@ int series_optimize(struct objlist *ob1, struct nlist *tp1, int idx1, } typedef struct _propsort { - double value; + double value; /* Primary sorting value */ + double avalue; /* Secondary sorting value */ + double slop; /* Delta for accepting equality */ int idx; unsigned char flags; struct objlist *ob; } propsort; /*--------------------------------------------------------------*/ -/* Property sorting routine used by qsort() */ +/* Property sorting routine used by qsort(). Sorts on "value" */ +/* unless the "value" for the two entries differs by less than */ +/* "slop", in which case the entries are sorted on "avalue". */ /*--------------------------------------------------------------*/ static int compsort(const void *p1, const void *p2) { propsort *s1, *s2; + double smax; s1 = (propsort *)p1; s2 = (propsort *)p2; - return (s1->value > s2->value) ? 1 : 0; + smax = fmax(s1->slop, s2->slop); + if (fabs(s1->value - s2->value) <= smax) + return (s1->avalue > s2->avalue) ? 1 : 0; + else + return (s1->value > s2->value) ? 1 : 0; } /*--------------------------------------------------------------*/ @@ -4094,7 +4103,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run) struct property *kl; struct valuelist *vl, *sl; int i, p, sval, merge_type; - double cval; + double cval, slop; obn = ob1->next; for (i = 0; i < idx1; i++) obn = obn->next; @@ -4107,7 +4116,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run) obp = obn; sval = 1; - cval = 0.0; + cval = slop = 0.0; for (i = 0; i < run; i++) { merge_type = MERGE_NONE; for (p = 0;; p++) { @@ -4121,24 +4130,35 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run) else { kl = (struct property *)HashLookup(vl->key, &(tp1->propdict)); if (kl && (kl->merge & (MERGE_S_ADD | MERGE_S_PAR))) { - if (vl->type == PROP_INTEGER) + if (vl->type == PROP_INTEGER) { cval = (double)vl->value.ival; - else + slop = (double)kl->slop.ival; + } + else { cval = vl->value.dval; + slop = kl->slop.dval; + } merge_type = kl->merge & (MERGE_S_ADD | MERGE_S_PAR); } } } if (merge_type == MERGE_S_ADD) { proplist[i].value = cval * (double)sval; + proplist[i].slop = slop; + proplist[i].avalue = 0; sl->value.ival = 1; } else if (merge_type == MERGE_S_PAR) { proplist[i].value = cval / (double)sval; + proplist[i].slop = slop; + proplist[i].avalue = 0; sl->value.ival = 1; } else { + /* Components which declare no series addition method stay unsorted */ proplist[i].value = (double)0; + proplist[i].avalue = (double)0; + proplist[i].slop = (double)1E-6; } proplist[i].idx = i; proplist[i].ob = obp; @@ -4236,32 +4256,31 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run) struct property *kl; struct valuelist *vl, *ml; int i, p, mval, merge_type, has_crit = FALSE; - char *subs_crit = NULL; - double cval; + char c, *subs_crit = NULL; + double cval, tval, aval, slop, tslop; obn = ob1->next; for (i = 0; i < idx1; i++) obn = obn->next; - // Create a structure of length (run) to hold critical property - // value and index. Then sort that list, then use the sorted - // indexes to sort the actual property linked list. - - // If there is no critical property listed, then it is still better - // to sort on any random property than on no properties. Note that - // this can (and should!) be made better by sorting on *all* - // properties, not just the first. Otherwise, circuit 1 can have, e.g., - // parallel transistors with W=1, L=1 and W=1, L=2 while circuit two - // has W=1, L=2 and W=1, L=1 and property matching will fail because - // sorting was done on W only. + /* Create a structure of length (run) to hold critical property + * value and index. Then sort that list, then use the sorted + * indexes to sort the actual property linked list. + * + * If there is no critical property listed, then it will sort on + * M first, and any additive property second, or any property at + * all if no additive property was found. It is doubtful that any + * use case requires more than two properties for sorting. + */ proplist = (propsort *)MALLOC(run * sizeof(propsort)); obp = obn; mval = 1; - cval = 0.0; + cval = aval = slop = 0.0; for (i = 0; i < run; i++) { merge_type = MERGE_NONE; ml = NULL; + c = (char)0; for (p = 0;; p++) { vl = &(obp->instance.props[p]); if (vl->type == PROP_ENDLIST) break; @@ -4277,85 +4296,72 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run) else if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR)) { if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) && (kl->type != vl->type)) - PromoteProperty(kl, vl); - if (vl->type == PROP_INTEGER) - cval = (double)vl->value.ival; - else if (vl->type == PROP_STRING) + PromoteProperty(kl, vl, obp, tp1); + if (vl->type == PROP_INTEGER) { + tval = (double)vl->value.ival; + tslop = (double)kl->slop.ival; + } + else if (vl->type == PROP_STRING) { /* This is unlikely---no method to merge string properties! */ - cval = (double)vl->value.string[0] + tval = (double)vl->value.string[0] + (double)vl->value.string[1] / 10.0; - else - cval = vl->value.dval; + tslop = (double)0; + } + else { + tval = vl->value.dval; + tslop = kl->slop.dval; + } - merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR); + if (merge_type == MERGE_NONE) + { + merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR); + cval = tval; + slop = tslop; + } + else { + if ((c == (char)0) || (toupper(vl->key[0]) > c)) { + c = toupper(vl->key[0]); + aval = tval; + } + } } + else { + if ((c == (char)0) || (toupper(vl->key[0]) > c)) { + if (vl->type == PROP_INTEGER) { + aval = (double)vl->value.ival; + c = toupper(vl->key[0]); + } + else if (vl->type == PROP_DOUBLE) { + aval = vl->value.dval; + c = toupper(vl->key[0]); + } + } + } } if (merge_type == MERGE_P_ADD) { proplist[i].value = cval * (double)mval; + proplist[i].avalue = aval; + proplist[i].slop = tslop; if (ml) ml->value.ival = 1; } else if (merge_type == MERGE_P_PAR) { proplist[i].value = cval / (double)mval; + proplist[i].avalue = aval; + proplist[i].slop = tslop; if (ml) ml->value.ival = 1; } else { - proplist[i].value = (double)0; + /* If there are no additive values, then sort first by M */ + /* and second by aval */ + proplist[i].value = (double)mval; + proplist[i].avalue = aval; + proplist[i].slop = (double)0; } proplist[i].idx = i; proplist[i].ob = obp; obp = obp->next; } - if (has_crit == FALSE) { - /* If no critical property was specified, then choose the first one found */ - /* and recalculate all the proplist values. */ - mval = 1; - obp = obn; - ml = NULL; - merge_type = MERGE_NONE; - for (i = 0; i < run; i++) { - for (p = 0;; p++) { - vl = &(obp->instance.props[p]); - if (vl->type == PROP_ENDLIST) break; - if (vl->key == NULL) continue; - if ((*matchfunc)(vl->key, "M")) { - mval = vl->value.ival; - ml = vl; - } - kl = (struct property *)HashLookup(vl->key, &(tp1->propdict)); - if (kl == NULL) continue; /* Ignored property */ - if (subs_crit == NULL) - subs_crit = vl->key; - if ((subs_crit != NULL) && (*matchfunc)(vl->key, subs_crit)) { - if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) && - (kl->type != vl->type)) - PromoteProperty(kl, vl); - if (vl->type == PROP_INTEGER) - cval = (double)vl->value.ival; - else if (vl->type == PROP_STRING) - /* In case property is non-numeric, sort by dictionary order */ - cval = (double)vl->value.string[0] - + (double)vl->value.string[1] / 10.0; - else - cval = vl->value.dval; - merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR); - } - } - if (merge_type == MERGE_P_ADD) { - proplist[i].value = cval * (double)mval; - if (ml) ml->value.ival = 1; - } - else if (merge_type == MERGE_P_PAR) { - proplist[i].value = cval / (double)mval; - if (ml) ml->value.ival = 1; - } - else { - proplist[i].value = 0; - } - obp = obp->next; - } - } - obn = obp; /* Link from last property */ qsort(&proplist[0], run, sizeof(propsort), compsort); @@ -4819,12 +4825,12 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series, else if (vl == NULL || vl2 == NULL) { if (vl == NULL) { if (kl->type != vlist[p][j]->type) - PromoteProperty(kl, vl2); + PromoteProperty(kl, vl2, ob2, tp); vl = &dfltvl; } else { if (kl->type != vlist[p][i]->type) - PromoteProperty(kl, vl); + PromoteProperty(kl, vl, ob2, tp); vl2 = &dfltvl; } dfltvl.type = kl->type; @@ -4846,7 +4852,7 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series, } // Additive properties do not need to be matched, since - // they can be combined. Critical paroperties must be + // they can be combined. Critical properties must be // matched. Properties with no merge behavior must match. ctype = clist[p][i]; @@ -5299,8 +5305,8 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1, } /* Promote properties as necessary to make sure they all match */ - if (kl1->type != vl1->type) PromoteProperty(kl1, vl1); - if (kl2->type != vl2->type) PromoteProperty(kl2, vl2); + if (kl1->type != vl1->type) PromoteProperty(kl1, vl1, tc1, tp1); + if (kl2->type != vl2->type) PromoteProperty(kl2, vl2, tc2, tp2); /* If kl1 and kl2 types differ, choose one type to target. Prefer */ /* double if either type is double, otherwise string. */ @@ -5320,8 +5326,8 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1, else klt = kl1; - if (vl2->type != klt->type) PromoteProperty(klt, vl2); - if (vl1->type != klt->type) PromoteProperty(klt, vl1); + if (vl2->type != klt->type) PromoteProperty(klt, vl2, tc2, tp2); + if (vl1->type != klt->type) PromoteProperty(klt, vl1, tc1, tp1); if (vl1->type != vl2->type) { if (do_print && (vl1->type != vl2->type)) { @@ -6207,6 +6213,7 @@ int ResolveAutomorphsByProperty() if ((E2->graph != E1->graph) && (E2->hashval == newhash)) { E2->hashval = orighash; C2--; + if (C2 == C1) break; } } } @@ -6215,6 +6222,7 @@ int ResolveAutomorphsByProperty() if ((E2->graph == E1->graph) && (E2->hashval == newhash)) { E2->hashval = orighash; C1--; + if (C1 == C2) break; } } } diff --git a/base/netgen.c b/base/netgen.c index 590db5d..ae57791 100644 --- a/base/netgen.c +++ b/base/netgen.c @@ -239,7 +239,8 @@ int TokGetValue(char *estr, struct nlist *parent, struct objlist *parprops, if (kl != NULL) { switch(kl->type) { case PROP_STRING: - result = ConvertStringToFloat(kl->pdefault.string, dval); + if (kl->pdefault.string != NULL) + result = ConvertStringToFloat(kl->pdefault.string, dval); break; case PROP_DOUBLE: case PROP_VALUE: @@ -268,529 +269,514 @@ int TokGetValue(char *estr, struct nlist *parent, struct objlist *parprops, /* single value, then replace the property type. */ /*--------------------------------------------------------------*/ -int ReduceExpressions(struct objlist *instprop, struct objlist *parprops, +int ReduceOneExpression(struct valuelist *kv, struct objlist *parprops, struct nlist *parent, int glob) { struct tokstack *expstack, *stackptr, *lptr, *nptr; - struct valuelist *kv; struct property *kl = NULL; char *estr, *tstr, *sstr; - int toktype, functype, i, result, modified, numlast, savetok; + int toktype, functype, result, modified, numlast, savetok; double dval; - if (instprop == NULL) return 0; // Nothing to do - if (instprop->type != PROPERTY) return -1; // Shouldn't happen + if (kv->type == PROP_EXPRESSION) + expstack = kv->value.stack; - for (i = 0;; i++) { + else if (kv->type == PROP_STRING) { - kv = &(instprop->instance.props[i]); - switch (kv->type) { - case PROP_ENDLIST: - break; + /* Compile the string into an expression stack */ + expstack = NULL; + estr = kv->value.string; + tstr = estr; - case PROP_INTEGER: - case PROP_DOUBLE: - case PROP_VALUE: - continue; - - case PROP_EXPRESSION: - expstack = kv->value.stack; - break; - - case PROP_STRING: - - expstack = NULL; - estr = kv->value.string; - tstr = estr; - - numlast = 0; - while (*tstr != '\0') { - switch(*tstr) { + numlast = 0; + while (*tstr != '\0') { + switch(*tstr) { - case '+': - if (numlast == 0) { - /* This is part of a number */ - dval = strtod(estr, &sstr); - if (sstr > estr && sstr > tstr) { - tstr = sstr - 1; - numlast = 1; - } - break; - } - /* Not a number, so must be arithmetic */ - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_PLUS, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; + case '+': + if (numlast == 0) { + /* This is part of a number */ + dval = strtod(estr, &sstr); + if (sstr > estr && sstr > tstr) { + tstr = sstr - 1; + numlast = 1; + } + break; + } + /* Not a number, so must be arithmetic */ + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_PLUS, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; - case '-': - if (numlast == 0) { - /* This is part of a number */ - dval = strtod(estr, &sstr); - if (sstr > estr && sstr > tstr) { - tstr = sstr - 1; - numlast = 1; - } - break; - } - /* Not a number, so must be arithmetic */ - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_MINUS, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': case '0': - /* Numerical value. Use strtod() to capture */ - if (numlast == 1) break; - dval = strtod(estr, &sstr); - if (sstr > estr && sstr > tstr) { - tstr = sstr - 1; - numlast = 1; - } - break; - - case '/': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_DIVIDE, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '*': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_MULTIPLY, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '(': - *tstr = '\0'; - - /* Check for predefined function keywords */ - - if (!strcmp(estr, "IF")) { - PushTok(TOK_FUNC_IF, NULL, &expstack); - } - else { - /* Treat as a parenthetical grouping */ - - result = TokGetValue(estr, parent, parprops, - glob, &dval); - if (result == 1) - PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) - PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_FUNC_OPEN, NULL, &expstack); - } - estr = tstr + 1; - numlast = 0; - break; - - case ')': - *tstr = '\0'; - - if (expstack == NULL) break; - savetok = expstack->toktype; - - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - - switch (savetok) { - case TOK_FUNC_THEN: - PushTok(TOK_FUNC_ELSE, NULL, &expstack); - break; - default: - PushTok(TOK_FUNC_CLOSE, NULL, &expstack); - break; - } + case '-': + if (numlast == 0) { + /* This is part of a number */ + dval = strtod(estr, &sstr); + if (sstr > estr && sstr > tstr) { + tstr = sstr - 1; numlast = 1; - estr = tstr + 1; + } + break; + } + /* Not a number, so must be arithmetic */ + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_MINUS, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '0': + /* Numerical value. Use strtod() to capture */ + if (numlast == 1) break; + dval = strtod(estr, &sstr); + if (sstr > estr && sstr > tstr) { + tstr = sstr - 1; + numlast = 1; + } + break; + + case '/': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_DIVIDE, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '*': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_MULTIPLY, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '(': + *tstr = '\0'; + + /* Check for predefined function keywords */ + + if (!strcmp(estr, "IF")) { + PushTok(TOK_FUNC_IF, NULL, &expstack); + } + else { + /* Treat as a parenthetical grouping */ + + result = TokGetValue(estr, parent, parprops, + glob, &dval); + if (result == 1) + PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) + PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_FUNC_OPEN, NULL, &expstack); + } + estr = tstr + 1; + numlast = 0; + break; + + case ')': + *tstr = '\0'; + + if (expstack == NULL) break; + savetok = expstack->toktype; + + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + + switch (savetok) { + case TOK_FUNC_THEN: + PushTok(TOK_FUNC_ELSE, NULL, &expstack); break; - - case '\'': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_SGL_QUOTE, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '"': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_DBL_QUOTE, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '{': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_GROUP_OPEN, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '}': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - PushTok(TOK_GROUP_CLOSE, NULL, &expstack); - estr = tstr + 1; - numlast = 1; - break; - - case '!': - if (*(tstr + 1) == '=') { - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, - glob, &dval); - if (result == 1) - PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) - PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_NE, NULL, &expstack); - } - numlast = 0; - break; - - case '=': - if (*(tstr + 1) == '=') { - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, - glob, &dval); - if (result == 1) - PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) - PushTok(TOK_STRING, estr, &expstack); - PushTok(TOK_EQ, NULL, &expstack); - numlast = 0; - } - break; - - case '>': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - - if (*(tstr + 1) == '=') { - PushTok(TOK_GE, NULL, &expstack); - tstr++; - } - else - PushTok(TOK_GT, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case '<': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - - if (*(tstr + 1) == '=') { - PushTok(TOK_LE, NULL, &expstack); - tstr++; - } - else - PushTok(TOK_LT, NULL, &expstack); - estr = tstr + 1; - numlast = 0; - break; - - case ',': - *tstr = '\0'; - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - if (expstack == NULL) break; - lptr = expstack; - while (lptr->next) { - lptr = lptr->next; - if (lptr->toktype == TOK_FUNC_THEN) { - PushTok(TOK_FUNC_ELSE, NULL, &expstack); - break; - } - else if (lptr->toktype == TOK_FUNC_IF) { - PushTok(TOK_FUNC_THEN, NULL, &expstack); - break; - } - } - estr = tstr + 1; - numlast = 0; - break; - default: + PushTok(TOK_FUNC_CLOSE, NULL, &expstack); break; } - tstr++; - } - result = TokGetValue(estr, parent, parprops, glob, &dval); - if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); - else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + numlast = 1; + estr = tstr + 1; + break; - FREE(kv->value.string); - kv->value.stack = expstack; - kv->type = PROP_EXPRESSION; - break; + case '\'': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_SGL_QUOTE, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '"': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_DBL_QUOTE, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '{': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_GROUP_OPEN, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '}': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + PushTok(TOK_GROUP_CLOSE, NULL, &expstack); + estr = tstr + 1; + numlast = 1; + break; + + case '!': + if (*(tstr + 1) == '=') { + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, + glob, &dval); + if (result == 1) + PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) + PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_NE, NULL, &expstack); + } + numlast = 0; + break; + + case '=': + if (*(tstr + 1) == '=') { + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, + glob, &dval); + if (result == 1) + PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) + PushTok(TOK_STRING, estr, &expstack); + PushTok(TOK_EQ, NULL, &expstack); + numlast = 0; + } + break; + + case '>': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + + if (*(tstr + 1) == '=') { + PushTok(TOK_GE, NULL, &expstack); + tstr++; + } + else + PushTok(TOK_GT, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case '<': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + + if (*(tstr + 1) == '=') { + PushTok(TOK_LE, NULL, &expstack); + tstr++; + } + else + PushTok(TOK_LT, NULL, &expstack); + estr = tstr + 1; + numlast = 0; + break; + + case ',': + *tstr = '\0'; + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); + if (expstack == NULL) break; + lptr = expstack; + while (lptr->next) { + lptr = lptr->next; + if (lptr->toktype == TOK_FUNC_THEN) { + PushTok(TOK_FUNC_ELSE, NULL, &expstack); + break; + } + else if (lptr->toktype == TOK_FUNC_IF) { + PushTok(TOK_FUNC_THEN, NULL, &expstack); + break; + } + } + estr = tstr + 1; + numlast = 0; + break; + + default: + break; + } + tstr++; } + result = TokGetValue(estr, parent, parprops, glob, &dval); + if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack); + else if (result == -1) PushTok(TOK_STRING, estr, &expstack); - // Find the beginning of the expression, which is the bottom of - // the stack. - for (stackptr = kv->value.stack; stackptr != NULL && + FREE(kv->value.string); + kv->value.stack = expstack; + kv->type = PROP_EXPRESSION; + } + + /* Find the beginning of the expression, which is the bottom of + * the stack. + */ + + for (stackptr = kv->value.stack; stackptr != NULL && stackptr->next != NULL; stackptr = stackptr->next); - // For each pass, start at the bottom and work forward - expstack = stackptr; + /* For each pass, start at the bottom and work forward. */ + expstack = stackptr; - modified = 1; - while (modified) { - double dval1, dval2; + modified = 1; + while (modified) { + double dval1, dval2; - modified = 0; + modified = 0; - // Reduce conditionals + // Reduce conditionals - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - switch (stackptr->toktype) { - case TOK_LE: - case TOK_LT: - case TOK_GE: - case TOK_GT: - case TOK_EQ: - case TOK_NE: - lptr = stackptr->last; - nptr = stackptr->next; - if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + switch (stackptr->toktype) { + case TOK_LE: + case TOK_LT: + case TOK_GE: + case TOK_GT: + case TOK_EQ: + case TOK_NE: + lptr = stackptr->last; + nptr = stackptr->next; + if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && (nptr->toktype == TOK_DOUBLE)) { - switch (stackptr->toktype) { - case TOK_LE: - if (nptr->data.dvalue <= lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - case TOK_LT: - if (nptr->data.dvalue < lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - case TOK_GE: - if (nptr->data.dvalue >= lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - case TOK_GT: - if (nptr->data.dvalue > lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - case TOK_EQ: - if (nptr->data.dvalue == lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - case TOK_NE: - if (nptr->data.dvalue != lptr->data.dvalue) { - stackptr->data.dvalue = 1.0; - } - else { - stackptr->data.dvalue = 0.0; - } - break; - } - modified = 1; - stackptr->toktype = TOK_DOUBLE; - stackptr->last = lptr->last; - if (lptr->last) lptr->last->next = stackptr; - else kv->value.stack = stackptr; - stackptr->next = nptr->next; - if (nptr->next) nptr->next->last = stackptr; - if (expstack == nptr) expstack = stackptr; - - FREE(nptr); - FREE(lptr); + switch (stackptr->toktype) { + case TOK_LE: + if (nptr->data.dvalue <= lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; + case TOK_LT: + if (nptr->data.dvalue < lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; + case TOK_GE: + if (nptr->data.dvalue >= lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; + case TOK_GT: + if (nptr->data.dvalue > lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; + case TOK_EQ: + if (nptr->data.dvalue == lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; + case TOK_NE: + if (nptr->data.dvalue != lptr->data.dvalue) { + stackptr->data.dvalue = 1.0; + } + else { + stackptr->data.dvalue = 0.0; + } + break; } - } + modified = 1; + stackptr->toktype = TOK_DOUBLE; + stackptr->last = lptr->last; + if (lptr->last) lptr->last->next = stackptr; + else kv->value.stack = stackptr; + stackptr->next = nptr->next; + if (nptr->next) nptr->next->last = stackptr; + if (expstack == nptr) expstack = stackptr; + + FREE(nptr); + FREE(lptr); + } } + } - // Reduce IF(a,b,c) + // Reduce IF(a,b,c) - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - struct tokstack *ifptr, *thenptr; - if (stackptr->toktype == TOK_FUNC_IF) { - ifptr = stackptr->last; - if (ifptr->toktype == TOK_DOUBLE) { - stackptr->toktype = TOK_FUNC_OPEN; - if (ifptr->data.dvalue == 0.0) { - /* Keep ELSE value, remove IF and THEN */ - for (thenptr = ifptr; thenptr->toktype != + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + struct tokstack *ifptr, *thenptr; + if (stackptr->toktype == TOK_FUNC_IF) { + ifptr = stackptr->last; + if (ifptr->toktype == TOK_DOUBLE) { + stackptr->toktype = TOK_FUNC_OPEN; + if (ifptr->data.dvalue == 0.0) { + /* Keep ELSE value, remove IF and THEN */ + for (thenptr = ifptr; thenptr->toktype != TOK_FUNC_ELSE; ) { - lptr = thenptr->last; - nptr = thenptr->next; - lptr->next = nptr; - nptr->last = lptr; - if (thenptr->toktype == TOK_STRING) - FREE(thenptr->data.string); - FREE(thenptr); - thenptr = lptr; - } - /* Free the TOK_FUNC_ELSE record */ lptr = thenptr->last; nptr = thenptr->next; lptr->next = nptr; nptr->last = lptr; + if (thenptr->toktype == TOK_STRING) + FREE(thenptr->data.string); FREE(thenptr); - modified = 1; + thenptr = lptr; } - else { - /* Keep THEN value, remove IF and ELSE */ - /* Free the conditional result value record */ - lptr = ifptr->last; - nptr = ifptr->next; - lptr->next = nptr; - nptr->last = lptr; - FREE(ifptr); - thenptr = nptr; + /* Free the TOK_FUNC_ELSE record */ + lptr = thenptr->last; + nptr = thenptr->next; + lptr->next = nptr; + nptr->last = lptr; + FREE(thenptr); + modified = 1; + } + else { + /* Keep THEN value, remove IF and ELSE */ + /* Free the conditional result value record */ + lptr = ifptr->last; + nptr = ifptr->next; + lptr->next = nptr; + nptr->last = lptr; + FREE(ifptr); + thenptr = nptr; - /* Free the TOK_FUNC_THEN record */ - lptr = thenptr->last; - nptr = thenptr->next; - lptr->next = nptr; - nptr->last = lptr; - FREE(thenptr); + /* Free the TOK_FUNC_THEN record */ + lptr = thenptr->last; + nptr = thenptr->next; + lptr->next = nptr; + nptr->last = lptr; + FREE(thenptr); - /* Free to end of IF block */ - for (thenptr = nptr->last; thenptr->toktype != + /* Free to end of IF block */ + for (thenptr = nptr->last; thenptr->toktype != TOK_FUNC_CLOSE; ) { - lptr = thenptr->last; - nptr = thenptr->next; - lptr->next = nptr; - nptr->last = lptr; - if (thenptr->toktype == TOK_STRING) - FREE(thenptr->data.string); - FREE(thenptr); - thenptr = lptr; - } - modified = 1; + lptr = thenptr->last; + nptr = thenptr->next; + lptr->next = nptr; + nptr->last = lptr; + if (thenptr->toktype == TOK_STRING) + FREE(thenptr->data.string); + FREE(thenptr); + thenptr = lptr; } + modified = 1; } } } + } - // Reduce (value) * (value) and (value) / (value) + // Reduce (value) * (value) and (value) / (value) - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - switch (stackptr->toktype) { - case TOK_MULTIPLY: - case TOK_DIVIDE: - lptr = stackptr->last; - nptr = stackptr->next; - if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + switch (stackptr->toktype) { + case TOK_MULTIPLY: + case TOK_DIVIDE: + lptr = stackptr->last; + nptr = stackptr->next; + if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && (nptr->toktype == TOK_DOUBLE)) { - if (stackptr->toktype == TOK_MULTIPLY) - stackptr->data.dvalue = nptr->data.dvalue * + if (stackptr->toktype == TOK_MULTIPLY) + stackptr->data.dvalue = nptr->data.dvalue * lptr->data.dvalue; - else - stackptr->data.dvalue = nptr->data.dvalue / + else + stackptr->data.dvalue = nptr->data.dvalue / lptr->data.dvalue; - modified = 1; - stackptr->toktype = TOK_DOUBLE; - stackptr->last = lptr->last; - if (lptr->last) lptr->last->next = stackptr; - else kv->value.stack = stackptr; - stackptr->next = nptr->next; - if (nptr->next) nptr->next->last = stackptr; - if (expstack == nptr) expstack = stackptr; + modified = 1; + stackptr->toktype = TOK_DOUBLE; + stackptr->last = lptr->last; + if (lptr->last) lptr->last->next = stackptr; + else kv->value.stack = stackptr; + stackptr->next = nptr->next; + if (nptr->next) nptr->next->last = stackptr; + if (expstack == nptr) expstack = stackptr; - FREE(nptr); - FREE(lptr); - } - } + FREE(nptr); + FREE(lptr); + } } + } - // Reduce (value) + (value) and (value) - (value) + // Reduce (value) + (value) and (value) - (value) - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - switch (stackptr->toktype) { - case TOK_PLUS: - case TOK_MINUS: - lptr = stackptr->last; - nptr = stackptr->next; - if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + switch (stackptr->toktype) { + case TOK_PLUS: + case TOK_MINUS: + lptr = stackptr->last; + nptr = stackptr->next; + if (lptr && nptr && (lptr->toktype == TOK_DOUBLE) && (nptr->toktype == TOK_DOUBLE)) { - if (stackptr->toktype == TOK_PLUS) - stackptr->data.dvalue = nptr->data.dvalue + + if (stackptr->toktype == TOK_PLUS) + stackptr->data.dvalue = nptr->data.dvalue + lptr->data.dvalue; - else - stackptr->data.dvalue = nptr->data.dvalue - + else + stackptr->data.dvalue = nptr->data.dvalue - lptr->data.dvalue; - modified = 1; - stackptr->toktype = TOK_DOUBLE; - stackptr->last = lptr->last; - if (lptr->last) lptr->last->next = stackptr; - else kv->value.stack = stackptr; - stackptr->next = nptr->next; - if (nptr->next) nptr->next->last = stackptr; - if (expstack == nptr) expstack = stackptr; + modified = 1; + stackptr->toktype = TOK_DOUBLE; + stackptr->last = lptr->last; + if (lptr->last) lptr->last->next = stackptr; + else kv->value.stack = stackptr; + stackptr->next = nptr->next; + if (nptr->next) nptr->next->last = stackptr; + if (expstack == nptr) expstack = stackptr; - FREE(nptr); - FREE(lptr); - } - } + FREE(nptr); + FREE(lptr); + } } + } - // Reduce {value}, (value), and 'value' + // Reduce {value}, (value), and 'value' - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - switch (stackptr->toktype) { - case TOK_DOUBLE: - lptr = stackptr->last; - nptr = stackptr->next; - if (lptr && nptr && + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + switch (stackptr->toktype) { + case TOK_DOUBLE: + lptr = stackptr->last; + nptr = stackptr->next; + if (lptr && nptr && (((nptr->toktype == TOK_FUNC_OPEN) && (lptr->toktype == TOK_FUNC_CLOSE)) || ((nptr->toktype == TOK_GROUP_OPEN) && @@ -800,75 +786,103 @@ int ReduceExpressions(struct objlist *instprop, struct objlist *parprops, ((nptr->toktype == TOK_SGL_QUOTE) && (lptr->toktype == TOK_SGL_QUOTE)))) { - modified = 1; - stackptr->last = lptr->last; - if (lptr->last) lptr->last->next = stackptr; - else kv->value.stack = stackptr; - stackptr->next = nptr->next; - if (nptr->next) nptr->next->last = stackptr; - if (expstack == nptr) expstack = stackptr; + modified = 1; + stackptr->last = lptr->last; + if (lptr->last) lptr->last->next = stackptr; + else kv->value.stack = stackptr; + stackptr->next = nptr->next; + if (nptr->next) nptr->next->last = stackptr; + if (expstack == nptr) expstack = stackptr; - FREE(nptr); - FREE(lptr); - } - break; - } + FREE(nptr); + FREE(lptr); + } + break; } + } - // Replace value if string can be substituted with a number + // Replace value if string can be substituted with a number - for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { - switch (stackptr->toktype) { - case TOK_STRING: - result = TokGetValue(stackptr->data.string, parent, + for (stackptr = expstack; stackptr != NULL; stackptr = stackptr->last) { + switch (stackptr->toktype) { + case TOK_STRING: + result = TokGetValue(stackptr->data.string, parent, parprops, glob, &dval); - if (result == 1) { - stackptr->toktype = TOK_DOUBLE; - FREE(stackptr->data.string); - stackptr->data.dvalue = dval; - modified = 1; - } - break; - } + if (result == 1) { + stackptr->toktype = TOK_DOUBLE; + FREE(stackptr->data.string); + stackptr->data.dvalue = dval; + modified = 1; + } + break; } } - - // Replace the expression with the reduced expression or - // value. - - expstack = kv->value.stack; // Now pointing at the end - - if (expstack && expstack->next == NULL) { - if (expstack->toktype == TOK_DOUBLE) { - kv->type = PROP_DOUBLE; - kv->value.dval = expstack->data.dvalue; - } - else if (expstack->toktype == TOK_STRING) { - kv->type = PROP_STRING; - kv->value.string = strsave(expstack->data.string); - } - } - else { - // Still an expression; do nothing - } - - // Free up the stack if it's not being used - - if (kv->type != PROP_EXPRESSION) - { - while (expstack != NULL) { - nptr = expstack->next; - if (expstack->toktype == TOK_STRING) - FREE(expstack->data.string); - FREE(expstack); - expstack = nptr; - } - } - - if (kv->type == PROP_ENDLIST) - break; } + // Replace the expression with the reduced expression or + // value. + + expstack = kv->value.stack; // Now pointing at the end + + if (expstack && expstack->next == NULL) { + if (expstack->toktype == TOK_DOUBLE) { + kv->type = PROP_DOUBLE; + kv->value.dval = expstack->data.dvalue; + } + else if (expstack->toktype == TOK_STRING) { + kv->type = PROP_STRING; + kv->value.string = strsave(expstack->data.string); + } + } + else { + // Still an expression; do nothing + } + + // Free up the stack if it's not being used + + if (kv->type != PROP_EXPRESSION) + { + while (expstack != NULL) { + nptr = expstack->next; + if (expstack->toktype == TOK_STRING) + FREE(expstack->data.string); + FREE(expstack); + expstack = nptr; + } + } + + return 0; +} + +/*----------------------------------------------------------------------*/ +/* Wrapper around the above routine, to reduce all expressions in a */ +/* property list. */ +/*----------------------------------------------------------------------*/ + +int ReduceExpressions(struct objlist *instprop, struct objlist *parprops, + struct nlist *parent, int glob) { + + struct valuelist *kv; + int i; + + if (instprop == NULL) return 0; // Nothing to do + if (instprop->type != PROPERTY) return -1; // Shouldn't happen + + for (i = 0;; i++) { + kv = &(instprop->instance.props[i]); + if (kv->type == PROP_ENDLIST) break; + switch (kv->type) + { + case PROP_INTEGER: + case PROP_DOUBLE: + case PROP_VALUE: + continue; + + default: + ReduceOneExpression(kv, parprops, parent, glob); + break; + } + } return 0; } @@ -2163,9 +2177,13 @@ int SetPropertyDefault(struct property *prop, struct valuelist *vl) /* to integers, and strings that are not numbers must be left as */ /* strings. Return 1 if the property was promoted successfully, and 0 */ /* if no promotion was possible, and -1 if passed a bad argument. */ +/* */ +/* NOTE: Arguments ob and tc are only used in the case of evaluating */ +/* an expression, used to generate a derived property. */ /*----------------------------------------------------------------------*/ -int PromoteProperty(struct property *prop, struct valuelist *vl) +int PromoteProperty(struct property *prop, struct valuelist *vl, + struct objlist *ob, struct nlist *tc) { char tstr[256]; int ival, result; @@ -2174,6 +2192,9 @@ int PromoteProperty(struct property *prop, struct valuelist *vl) if (prop == NULL || vl == NULL) return -1; if (prop->type == vl->type) return 1; /* Nothing to do */ result = 0; + if (prop->type == PROP_EXPRESSION) { + ReduceOneExpression(vl, ob, tc, FALSE); + } switch (prop->type) { case PROP_STRING: switch (vl->type) { diff --git a/base/netgen.h b/base/netgen.h index 20e55f3..b60893c 100644 --- a/base/netgen.h +++ b/base/netgen.h @@ -43,7 +43,8 @@ extern int PropertyMerge(char *name, int fnum, char *key, int merge_type, int merge_mask); extern void ResolveProperties(char *name1, int file1, char *name2, int file2); extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from); -extern int PromoteProperty(struct property *, struct valuelist *); +extern int PromoteProperty(struct property *, struct valuelist *, + struct objlist *, struct nlist *); extern int SetPropertyDefault(struct property *, struct valuelist *); extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr); extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops, diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index ccab280..cd6b262 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -2457,8 +2457,7 @@ _netcmp_run(ClientData clientData, automorphisms = ResolveAutomorphsByProperty(); if (automorphisms == 0) Fprintf(stdout, "Netlists match uniquely.\n"); - else { - + else if (automorphisms > 0) { // Next, attempt to resolve automorphisms uniquely by // using the pin names automorphisms = ResolveAutomorphsByPin(); @@ -2466,14 +2465,17 @@ _netcmp_run(ClientData clientData, if (automorphisms == 0) Fprintf(stdout, "Netlists match uniquely.\n"); - else + else if (automorphisms > 0) { // Anything left is truly indistinguishable Fprintf(stdout, "Netlists match with %d symmetr%s.\n", automorphisms, (automorphisms == 1) ? "y" : "ies"); - while ((automorphisms = ResolveAutomorphisms()) > 0); - if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); - else Fprintf(stdout, "Circuits match correctly.\n"); + while ((automorphisms = ResolveAutomorphisms()) > 0); + } + if (automorphisms == -1) + Fprintf(stdout, "Netlists do not match.\n"); + else + Fprintf(stdout, "Circuits match correctly.\n"); } if (PropertyErrorDetected) { Fprintf(stdout, "There were property errors.\n"); @@ -3341,10 +3343,10 @@ _netcmp_property(ClientData clientData, int result, index, idx2; char *suboptions[] = { - "integer", "double", "value", "string", NULL + "integer", "double", "value", "string", "expression", NULL }; enum SubOptionIdx { - INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX + INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX, EXPRESSION_IDX }; /* Note: "merge" has been deprecated, but kept for backwards compatibility. */ @@ -3733,6 +3735,11 @@ _netcmp_property(ClientData clientData, PropertyString(tp->name, fnum, Tcl_GetString(tobj1), ival, NULL); break; + case EXPRESSION_IDX: + PropertyString(tp->name, fnum, + Tcl_GetString(tobj1), 0, + Tcl_GetString(tobj3)); + break; } break; }