|
|
1.1 ! root 1: /* ! 2: Expression.m ! 3: ! 4: The Expression class is implemented using a grammar generated by the Unix ! 5: program yacc, and a lexical scanner generated by the Unix program lex. ! 6: The only interface between these parsing modules and this class is the ! 7: function _EXPParseExpression, which actually performs the parse. The ! 8: results of the parse are returned in two data structures: a parse tree ! 9: representing the expression and a hashtable which holds that variables ! 10: found in the expression. ! 11: ! 12: The parse tree's nodes are Term structs (declared in exprDefs.h). There ! 13: is a node for each piece of the expression. For example, when the ! 14: expression "A+5" is parsed, three nodes result. The top node of the tree ! 15: represents the binary operator "+". This top node has two subnodes. The ! 16: first subnode represents the variable "A". The second represents the ! 17: integer constant "5". Since variables can appear more than once in an ! 18: expression, their nodes may occur more than once in the parse tree. ! 19: ! 20: A parse tree is evaluated by doing a depth first traversal of the tree, ! 21: bubbling up the results of each sub-tree until the final value rises ! 22: to the top. ! 23: ! 24: You may freely copy, distribute, and reuse the code in this example. ! 25: NeXT disclaims any warranty of any kind, expressed or implied, as to its ! 26: fitness for any particular use. ! 27: */ ! 28: ! 29: #import "Graph.h" ! 30: ! 31: /* declaration of methods static to this class */ ! 32: @interface Expression(ExpressionPrivate) ! 33: - (int)_updateResults; ! 34: - (EXPTermPtr)_addVarTerm:(const char *)name; ! 35: @end ! 36: ! 37: @implementation Expression ! 38: ! 39: /* function which can be applied to parse tree nodes with applyToTerms() */ ! 40: typedef void ParseTreeFunc(void *info, Term *term); ! 41: ! 42: static void freeContents(Expression *self); ! 43: static void applyToTerms(Term *t, ParseTreeFunc *func, void *data, int mask); ! 44: static void updateTerm(void *data, Term *t); ! 45: static float evalTerm(Term *t, int *indices); ! 46: static Term *termOfVar(Expression *self, const char *varName); ! 47: static NXHashTable *makeBuiltInFuncTable(NXZone *zone); ! 48: static NXHashTable *getBuiltInFuncs(void); ! 49: static Function *addFuncTerm(NXHashTable *table, const char *name, int min, int max, EXPTermEvalFunc *func); ! 50: static void safeFree(void **data); ! 51: static EXPTermEvalFunc sinStub, cosStub, tanStub; ! 52: static EXPTermEvalFunc asinStub, acosStub, atanStub; ! 53: static EXPTermEvalFunc expStub, lnStub, sqrtStub, sumStub; ! 54: static char *termName(const Term *t); ! 55: static void FunctionFree(const void *info, void *data); ! 56: static unsigned VarTermHash(const void *info, const void *data); ! 57: static int VarTermCompare(const void *info, const void *data1, const void *data2); ! 58: ! 59: /* ! 60: * Shared table of built in functions. All Expressions which haven't had ! 61: * any application functions added to them shared this table, which contains ! 62: * just the built in functions. ! 63: */ ! 64: static NXHashTable *BuiltInFuncTable = NULL; ! 65: ! 66: /* data for a built in function */ ! 67: typedef struct _BuiltInFunc { ! 68: const char *name; /* name of the function */ ! 69: EXPTermEvalFunc *func; /* proc to call for evaluation */ ! 70: int minArgs; ! 71: int maxArgs; ! 72: } BuiltInFunc; ! 73: ! 74: /* table of built in functions */ ! 75: static const BuiltInFunc FuncList[] = { ! 76: {"sin", &sinStub, 1, 1}, ! 77: {"cos", &cosStub, 1, 1}, ! 78: {"tan", &tanStub, 1, 1}, ! 79: {"asin", &asinStub, 1, 1}, ! 80: {"acos", &acosStub, 1, 1}, ! 81: {"atan", &atanStub, 1, 1}, ! 82: {"exp", &expStub, 1, 1}, ! 83: {"ln", &lnStub, 1, 1}, ! 84: {"sqrt", &sqrtStub, 1, 1}, ! 85: {"sum", &sumStub, 1, -1} ! 86: }; ! 87: ! 88: #define NUM_BUILTIN_FUNCS (sizeof(FuncList)/sizeof(BuiltInFunc)) ! 89: ! 90: /* prototype used to create hashtables of variable terms */ ! 91: static NXHashTablePrototype VarTermProto = {&VarTermHash, &VarTermCompare, (void (*)(const void *info, void *data))&_EXPFreeTerm, 0}; ! 92: ! 93: - init { ! 94: [super init]; ! 95: resolution = 1; ! 96: dimensions = 1; ! 97: varTerms = NXCreateHashTableFromZone(VarTermProto, 0, NULL, [self zone]); ! 98: return self; ! 99: } ! 100: ! 101: - free { ! 102: freeContents(self); ! 103: /* if we have a function table and its not the shared one */ ! 104: if (validFuncs && validFuncs != BuiltInFuncTable) ! 105: NXFreeHashTable(validFuncs); ! 106: return [super free]; ! 107: } ! 108: ! 109: - (BOOL)parse:(const char *)expressionString { ! 110: NXZone *zone; ! 111: ! 112: zone = [self zone]; ! 113: /* clear away any results from a previous parse */ ! 114: freeContents(self); ! 115: varTerms = NXCreateHashTableFromZone(VarTermProto, 0, NULL, zone); ! 116: resultsValid = NO; ! 117: text = NXCopyStringBufferFromZone(expressionString, zone); ! 118: if (!validFuncs) ! 119: validFuncs = getBuiltInFuncs(); ! 120: return _EXPParseExpression(text, validFuncs, &parseTree, varTerms, zone); ! 121: } ! 122: ! 123: - (const char *)text { ! 124: return text; ! 125: } ! 126: ! 127: - setResolution:(int)count { ! 128: if (resolution != count) { ! 129: /* ! 130: * Changing the resolution means we have to recalculate next time ! 131: * we're asked for results ! 132: */ ! 133: resultsValid = NO; ! 134: resolution = count; ! 135: } ! 136: return self; ! 137: } ! 138: ! 139: - (int)resolution { ! 140: return resolution; ! 141: } ! 142: ! 143: - setVar:(const char *)varName value:(float)val { ! 144: Term *t; ! 145: ! 146: t = termOfVar(self, varName); ! 147: if (!t) ! 148: t = [self _addVarTerm:varName]; ! 149: /* ! 150: * If the term was previously a vector term, we change it to a variable ! 151: * term (one that has a single value). ! 152: */ ! 153: if (t->tag == vectorTerm) { ! 154: t->tag = varTerm; ! 155: safeFree((void **)&t->data.vector.vals); ! 156: t->data.var.name = t->data.vector.name; ! 157: resultsValid = NO; ! 158: } ! 159: NX_ASSERT(t->tag == varTerm, "Invalid term type in setVar:value:"); ! 160: if (t->data.var.val != val) { ! 161: t->data.var.val = val; ! 162: resultsValid = NO; /* must recalc after a var is set */ ! 163: } ! 164: return self; ! 165: } ! 166: ! 167: - (float)varValue:(const char *)varName { ! 168: Term *t; ! 169: ! 170: t = termOfVar(self, varName); ! 171: if (t) { ! 172: if (t->tag == varTerm) ! 173: return t->data.var.val; ! 174: else ! 175: NX_RAISE(expErrInvalidVarType, self, (void *)varName); ! 176: } else ! 177: NX_RAISE(expErrInvalidVarName, self, (void *)varName); ! 178: } ! 179: ! 180: - setVar:(const char *)varName vector:(float *)vals numVals:(int)count { ! 181: Term *t; ! 182: ! 183: resultsValid = NO; ! 184: t = termOfVar(self, varName); ! 185: if (!t) ! 186: t = [self _addVarTerm:varName]; ! 187: /* ! 188: * If the term was previously a non-vector variable, we change it to a ! 189: * vector variable term. ! 190: */ ! 191: if (t->tag == varTerm) { ! 192: t->tag = vectorTerm; ! 193: t->data.vector.name = t->data.var.name; ! 194: t->data.vector.hasRange = NO; ! 195: t->data.vector.vals = NULL; ! 196: t->data.vector.resolution = 0; ! 197: t->data.vector.dimension = 0; ! 198: } ! 199: NX_ASSERT(t->tag == vectorTerm, "Invalid term type in setVar:vector:"); ! 200: safeFree((void **)&t->data.vector.vals); ! 201: t->data.vector.resolution = count; ! 202: t->data.vector.vals = vals; ! 203: t->data.vector.changed = YES; ! 204: resolution = count; ! 205: return self; ! 206: } ! 207: ! 208: - varVector:(const char *)varName vector:(float **)vals numVals:(int *)count { ! 209: Term *t; ! 210: ! 211: t = termOfVar(self, varName); ! 212: if (t) { ! 213: if (t->tag == vectorTerm) { ! 214: updateTerm(self, t); ! 215: *count = t->data.vector.resolution; ! 216: *vals = t->data.vector.vals; ! 217: } else ! 218: NX_RAISE(expErrInvalidVarType, self, (void *)varName); ! 219: } else ! 220: NX_RAISE(expErrInvalidVarName, self, (void *)varName); ! 221: return self; ! 222: } ! 223: ! 224: - setVar:(const char *)varName min:(float)minVal max:(float)maxVal { ! 225: Term *t; ! 226: ! 227: if (minVal > maxVal) ! 228: NX_RAISE(expErrMinMax, self, NULL); ! 229: t = termOfVar(self, varName); ! 230: if (!t) ! 231: t = [self _addVarTerm:varName]; ! 232: /* ! 233: * If the term was previously a non-vector variable, we change it to a ! 234: * vector variable term. ! 235: */ ! 236: if (t->tag == varTerm) { ! 237: t->tag = vectorTerm; ! 238: t->data.vector.name = t->data.var.name; ! 239: t->data.vector.hasRange = YES; ! 240: t->data.vector.vals = NULL; ! 241: t->data.vector.changed = YES; ! 242: t->data.vector.dimension = 0; ! 243: } ! 244: NX_ASSERT(t->tag == vectorTerm, "Invalid term type in setVar:min:max:"); ! 245: ! 246: /* ! 247: * We optimize and do nothing if the passed values are the same as our ! 248: * current values. ! 249: */ ! 250: if (t->data.vector.changed || t->data.vector.min != minVal || t->data.vector.max != maxVal) { ! 251: safeFree((void **)&t->data.vector.vals); ! 252: t->data.vector.resolution = 0; ! 253: t->data.vector.min = minVal; ! 254: t->data.vector.max = maxVal; ! 255: t->data.vector.changed = YES; ! 256: resultsValid = NO; ! 257: } ! 258: return self; ! 259: } ! 260: ! 261: - var:(const char *)varName min:(float *)minVal max:(float *)maxVal { ! 262: Term *t; ! 263: ! 264: t = termOfVar(self, varName); ! 265: if (t) { ! 266: if (t->tag == vectorTerm) { ! 267: updateTerm(self, t); ! 268: *minVal = t->data.vector.min; ! 269: *maxVal = t->data.vector.max; ! 270: } else ! 271: NX_RAISE(expErrInvalidVarType, self, (void *)varName); ! 272: } else ! 273: NX_RAISE(expErrInvalidVarName, self, (void *)varName); ! 274: return self; ! 275: } ! 276: ! 277: - setVar:(const char *)varName dimension:(short)dimensionNum { ! 278: Term *t; ! 279: ! 280: if (dimensionNum < 0 || dimensionNum >= dimensions) ! 281: NX_RAISE(expInvalidDimension, self, (void *)varName); ! 282: resultsValid = NO; ! 283: t = termOfVar(self, varName); ! 284: if (!t) ! 285: t = [self _addVarTerm:varName]; ! 286: /* ! 287: * If the term was previously a non-vector variable, we change it to a ! 288: * vector variable term. ! 289: */ ! 290: if (t->tag == varTerm) { ! 291: t->tag = vectorTerm; ! 292: t->data.vector.name = t->data.var.name; ! 293: t->data.vector.hasRange = YES; ! 294: t->data.vector.vals = NULL; ! 295: t->data.vector.changed = YES; ! 296: t->data.vector.dimension = 0; ! 297: } ! 298: NX_ASSERT(t->tag == vectorTerm, "Invalid term type in setVar:dimension:"); ! 299: t->data.vector.dimension = dimensionNum; ! 300: return self; ! 301: } ! 302: ! 303: - var:(const char *)varName dimension:(short *)dimensionNum { ! 304: Term *t; ! 305: ! 306: t = termOfVar(self, varName); ! 307: if (t) { ! 308: if (t->tag == vectorTerm) ! 309: *dimensionNum = t->data.vector.dimension; ! 310: else ! 311: NX_RAISE(expErrInvalidVarType, self, (void *)varName); ! 312: } else ! 313: NX_RAISE(expErrInvalidVarName, self, (void *)varName); ! 314: return self; ! 315: } ! 316: ! 317: - (float)resultValue { ! 318: [self _updateResults]; ! 319: return *results; ! 320: } ! 321: ! 322: - resultsVector:(float **)vals numVals:(int *)count { ! 323: *count = [self _updateResults]; ! 324: *vals = results; ! 325: return self; ! 326: } ! 327: ! 328: - resultsMin:(float *)minVal max:(float *)maxVal { ! 329: [self _updateResults]; ! 330: *minVal = resultsMin; ! 331: *maxVal = resultsMax; ! 332: return self; ! 333: } ! 334: ! 335: - setDimensions:(short)count { ! 336: if (dimensions != count) ! 337: resultsValid = NO; ! 338: dimensions = count; ! 339: return self; ! 340: } ! 341: ! 342: - (short)dimensions { ! 343: return dimensions; ! 344: } ! 345: ! 346: /* ! 347: * Since the varables are all in a NXHashTable, we just use the NXHashTable ! 348: * functions to enumerate through the names of the variables. ! 349: */ ! 350: ! 351: - (EXPEnumState)beginVariableEnumeration { ! 352: NXHashState *state; ! 353: ! 354: state = NXZoneMalloc([self zone], sizeof(NXHashState)); ! 355: *state = NXInitHashState(varTerms); ! 356: return state; ! 357: } ! 358: ! 359: - (const char *)nextVariable:(EXPEnumState)state { ! 360: const Term *t; ! 361: ! 362: if (NXNextHashState(varTerms, (NXHashState *)state, (void **)&t)) ! 363: return termName(t); ! 364: else ! 365: return NULL; ! 366: } ! 367: ! 368: - (void)endVariableEnumeration:(EXPEnumState)state { ! 369: free(state); ! 370: } ! 371: ! 372: - addFuncTerm:(const char *)name minArgs:(int)min maxArgs:(int)max ! 373: evalFunc:(EXPTermEvalFunc *)func { ! 374: Function *existingType; ! 375: ! 376: /* ! 377: * If we dont have a function table yet, get one. If we do have one, but ! 378: * its the shared built in table, get a new one that we can safely modify. ! 379: */ ! 380: if (!validFuncs || validFuncs == BuiltInFuncTable) ! 381: validFuncs = makeBuiltInFuncTable([self zone]); ! 382: existingType = addFuncTerm(validFuncs, name, min, max, func); ! 383: if (existingType) ! 384: NX_RAISE(expFuncTypeInUse, self, existingType); ! 385: return self; ! 386: } ! 387: ! 388: - removeFuncTerm:(const char *)name { ! 389: Function *realFunc; ! 390: Function key; ! 391: ! 392: if (validFuncs) { ! 393: /* look up the func term by name in our table of functions */ ! 394: key.name = (char *)name; ! 395: if (realFunc = NXHashGet(validFuncs, &key)) { ! 396: /* ! 397: * If we are using the shared table of built ins, get a new table ! 398: * that we can safely modify (this covers the case of someone ! 399: * wishing to remove a built in function, possibly to redefine it). ! 400: */ ! 401: if (validFuncs == BuiltInFuncTable) { ! 402: validFuncs = makeBuiltInFuncTable([self zone]); ! 403: realFunc = NXHashGet(validFuncs, &key); ! 404: } ! 405: NXHashRemove(validFuncs, realFunc); ! 406: free(realFunc); ! 407: return self; ! 408: } ! 409: } ! 410: return self; ! 411: } ! 412: ! 413: /* ! 414: * This is a utililty routine that recursively applies a function to all ! 415: * terms of a parse tree. data is a blind pointer that is simply passed ! 416: * along through the function call. The function is only called on terms ! 417: * whose type match the given mask. ! 418: */ ! 419: static void applyToTerms(Term *t, ParseTreeFunc *func, void *data, int mask) { ! 420: int i; ! 421: Term **tPtr; ! 422: ! 423: for (i = t->numSubterms, tPtr = t->subterms; i--; tPtr++) ! 424: applyToTerms(*tPtr, func, data, mask); ! 425: if (t->tag & mask) ! 426: (*func)(data, t); ! 427: } ! 428: ! 429: /* ! 430: * Empties the contents of the Expression. Since the variable terms can ! 431: * exist multiple times in the tree, we first run through the tree and ! 432: * free all the nodes except that variable nodes. Then we free the hash ! 433: * table of variable terms, including the terms themselves. ! 434: */ ! 435: static void freeContents(Expression *self) { ! 436: safeFree((void **)&self->text); ! 437: /* free the non-variable terms */ ! 438: if (self->parseTree) ! 439: applyToTerms(self->parseTree, _EXPFreeTerm, NULL, ~(varTerm|vectorTerm)); ! 440: /* free the shared variable terms */ ! 441: NXFreeHashTable(self->varTerms); ! 442: safeFree((void **)&self->results); ! 443: } ! 444: ! 445: /* ! 446: * Allocates a new term of the given type, with room for subterms. The ! 447: * subterms themselves follow as a variable number of arguments. The are ! 448: * copied into the subterms list of the new term. ! 449: */ ! 450: Term *_EXPAllocTerm(NXZone *zone, TermTag tag, int numSubterms, ...) { ! 451: Term *t; ! 452: int i; ! 453: va_list args; ! 454: ! 455: t = NXZoneCalloc(zone, sizeof(Term) + (numSubterms-1) * sizeof(Term *), 1); ! 456: t->tag = tag; ! 457: t->numSubterms = numSubterms; ! 458: va_start(args, numSubterms); ! 459: for (i = 0; i < numSubterms; i++) ! 460: t->subterms[i] = va_arg(args, Term *); ! 461: va_end(args); ! 462: return t; ! 463: } ! 464: ! 465: /* ! 466: * Frees a term and any associated data. This routine can be used as the ! 467: * free function of a NXHashTable prototype, or as a proc passed to ! 468: * applyToTerms(). ! 469: */ ! 470: void _EXPFreeTerm(void *info, Term *data) { ! 471: Term *t = (Term *)data; ! 472: ! 473: if (t->tag == vectorTerm) { ! 474: free(t->data.vector.vals); ! 475: free(t->data.vector.name); ! 476: } else if (t->tag == varTerm) ! 477: free(t->data.var.name); ! 478: free(t); ! 479: } ! 480: ! 481: /* ! 482: * Makes sure a term is up to date. Since terms recalculate any internal ! 483: * state lazily, this must be called before making use of a term's value. ! 484: * We apply this function recursively to all terms before evaluating an ! 485: * Expression, and apply to any term if we are asked to return the values ! 486: * held within the term (for example, from -varVector:vector:numVals:). ! 487: */ ! 488: static void updateTerm(void *data, Term *t) { ! 489: Expression *self = data; ! 490: ! 491: if (t->tag != vectorTerm) ! 492: return; /* only vector terms require work to stay up to date */ ! 493: ! 494: /* Ensure this term has the same resolution as the rest of the Expression. */ ! 495: if (self->resolution != t->data.vector.resolution) { ! 496: /* ! 497: * We can change its resolution if we interpolate values for this term ! 498: * within a range. Else if we were passed a list of values for this ! 499: * term, its an exception if the resolution is no longer in sync. ! 500: */ ! 501: if (t->data.vector.hasRange) { ! 502: safeFree((void **)&t->data.vector.vals); ! 503: t->data.vector.vals = NXZoneMalloc([self zone], ! 504: self->resolution * sizeof(float)); ! 505: t->data.vector.resolution = self->resolution; ! 506: t->data.vector.changed = YES; /* remember to re-interpolate */ ! 507: } else ! 508: NX_RAISE(expErrResolutionMismatch, self, ! 509: (void *)t->data.vector.name); ! 510: } ! 511: ! 512: if (t->data.vector.changed) { ! 513: if (t->data.vector.hasRange) { ! 514: /* interpolate a list of values between min and max */ ! 515: int i; ! 516: float delta; ! 517: float *val, *prevVal; ! 518: ! 519: i = self->resolution - 1; ! 520: if (i) { ! 521: delta = (t->data.vector.max - t->data.vector.min) / i; ! 522: prevVal = t->data.vector.vals; ! 523: *prevVal = t->data.vector.min; ! 524: val = prevVal + 1; ! 525: while (i--) ! 526: *val++ = *prevVal++ + delta; ! 527: *(val-1) = t->data.vector.max; /* to be sure we hit max */ ! 528: } else ! 529: *t->data.vector.vals = t->data.vector.min; ! 530: } else { ! 531: /* scan the list of values passed in to find the min and max */ ! 532: int i; ! 533: float newMin, newMax; ! 534: float *val; ! 535: ! 536: val = t->data.vector.vals; ! 537: newMin = newMax = *val++; ! 538: for (i = t->data.vector.resolution; i--; val++) { ! 539: if (*val > newMax) ! 540: newMax = *val; ! 541: else if (*val < newMin) ! 542: newMin = *val; ! 543: } ! 544: t->data.vector.min = newMin; ! 545: t->data.vector.max = newMax; ! 546: } ! 547: t->data.vector.changed = NO; /* we're now up to date */ ! 548: } ! 549: } ! 550: ! 551: /* ! 552: * Ensures that the results calculated for this Expression are up to date. ! 553: * We first make sure all the terms in the parse tree are up to date by ! 554: * applying updateTerm() to all of them. We then evaluate the Expression ! 555: * for every n times, depending on the resolution, storing all the results ! 556: * and calculating their min and max. ! 557: */ ! 558: - (int)_updateResults { ! 559: #define MAX_INDICES 10 ! 560: int i, j; ! 561: float *f; ! 562: int indicesBuffer[MAX_INDICES]; ! 563: int *indices = indicesBuffer; ! 564: int totalVals; ! 565: NXZone *zone = [self zone]; ! 566: ! 567: totalVals = resolution; ! 568: for (i = 1; i < dimensions; i++) ! 569: totalVals *= resolution; ! 570: if (!resultsValid) { ! 571: if (parseTree) { ! 572: applyToTerms(self->parseTree, updateTerm, self, -1); ! 573: safeFree((void **)&results); ! 574: results = NXZoneMalloc(zone, sizeof(float) * totalVals); ! 575: if (dimensions > MAX_INDICES) ! 576: indices = NXZoneMalloc(zone, sizeof(int) * dimensions); ! 577: bzero(indices, sizeof(int) * dimensions); ! 578: *results = evalTerm(parseTree, indices); ! 579: resultsMin = *results; ! 580: resultsMax = *results; ! 581: for (i = 1, f = results + 1; i < totalVals; i++, f++) { ! 582: ! 583: /* ! 584: * Increment the indices. The first one always changes. The ! 585: * others change only when the previous one wraps around (like ! 586: * an odometer with base = resolution). ! 587: */ ! 588: for (j = 0; j < dimensions; j++) { ! 589: indices[j] = (indices[j] + 1) % resolution; ! 590: if (indices[j]) ! 591: break; ! 592: } ! 593: ! 594: /* ! 595: * We pass the loop indices down through the evaluation recursion ! 596: * so vector terms can know which element of their vectors they ! 597: * should use for this evaluation. ! 598: */ ! 599: *f = evalTerm(parseTree, indices); ! 600: if (*f > resultsMax) ! 601: resultsMax = *f; ! 602: else if (*f < resultsMin) ! 603: resultsMin = *f; ! 604: } ! 605: if (dimensions > MAX_INDICES) ! 606: NXZoneFree(zone, indices); ! 607: } else ! 608: NX_RAISE(expErrNoText, self, NULL); ! 609: resultsValid = YES; ! 610: } ! 611: return totalVals; ! 612: } ! 613: ! 614: #define MAX_ARGS 50 ! 615: ! 616: /* ! 617: * Evaluates a particular term. In order to evaluate itself, any term with ! 618: * subterms must recursively evaluate those first. ! 619: */ ! 620: static float evalTerm(Term *t, int *indices) { ! 621: float result = 0.0; /* initted to quiet -Wall */ ! 622: float base; ! 623: float argsBuffer[MAX_ARGS]; ! 624: float *args; ! 625: int i; ! 626: ! 627: switch (t->tag) { ! 628: case constantTerm: ! 629: NX_ASSERT(t->numSubterms == 0, "Wrong #subterms in evalTerm"); ! 630: result = t->data.constant.val; ! 631: break; ! 632: case varTerm: ! 633: NX_ASSERT(t->numSubterms == 0, "Wrong #subterms in evalTerm"); ! 634: result = t->data.var.val; ! 635: break; ! 636: case vectorTerm: ! 637: NX_ASSERT(t->numSubterms == 0, "Wrong #subterms in evalTerm"); ! 638: result = t->data.vector.vals[indices[t->data.vector.dimension]]; ! 639: break; ! 640: case binOpTerm: ! 641: NX_ASSERT(t->numSubterms == 2 || ! 642: (t->data.binOp.op == '-' && t->numSubterms == 1), ! 643: "Wrong #subterms in evalTerm"); ! 644: switch (t->data.binOp.op) { ! 645: case '+': ! 646: result = evalTerm(t->subterms[0], indices) + ! 647: evalTerm(t->subterms[1], indices); ! 648: break; ! 649: case '-': ! 650: if (t->numSubterms == 2) ! 651: result = evalTerm(t->subterms[0], indices) - ! 652: evalTerm(t->subterms[1], indices); ! 653: else ! 654: result = - evalTerm(t->subterms[0], indices); ! 655: break; ! 656: case '*': ! 657: result = evalTerm(t->subterms[0], indices) * ! 658: evalTerm(t->subterms[1], indices); ! 659: break; ! 660: case '/': ! 661: result = evalTerm(t->subterms[0], indices) / ! 662: evalTerm(t->subterms[1], indices); ! 663: break; ! 664: case '%': ! 665: result = (int)rint(evalTerm(t->subterms[0], indices)) % ! 666: (int)rint(evalTerm(t->subterms[1], indices)); ! 667: break; ! 668: case '^': ! 669: /* optimize for raising to an integral power */ ! 670: if (t->subterms[1]->tag == constantTerm && ! 671: t->subterms[1]->data.constant.isInt && ! 672: t->subterms[1]->data.constant.val >= 1) { ! 673: result = base = evalTerm(t->subterms[0], indices); ! 674: for (i = t->subterms[1]->data.constant.val; --i; ) ! 675: result *= base; ! 676: } else ! 677: result = pow(evalTerm(t->subterms[0], indices), ! 678: evalTerm(t->subterms[1], indices)); ! 679: break; ! 680: default: ! 681: NX_ASSERT(FALSE, "Unknown binary op type in evalTerm"); ! 682: } ! 683: break; ! 684: case funcTerm: ! 685: /* ! 686: * For functions, we first ensure we have a large enough buffer ! 687: * for the values of all the arguments. If there are few enough ! 688: * arguments, we use a buffer on the stack instead of thrashing ! 689: * the heap. We then buffer up all the results of evaluating ! 690: * the arguments, and then pass this array of argument values ! 691: * to the proc that we use to evaluate this type of function. ! 692: */ ! 693: if (t->numSubterms > MAX_ARGS) ! 694: args = NXZoneMalloc(NXDefaultMallocZone(), ! 695: sizeof(float) * t->numSubterms); ! 696: else ! 697: args = argsBuffer; ! 698: for (i = 0; i < t->numSubterms; i++) ! 699: args[i] = evalTerm(t->subterms[i], indices); ! 700: result = t->data.func.type->evalFunc(t->numSubterms, args); ! 701: if (t->numSubterms > MAX_ARGS) ! 702: NXZoneFree(NXDefaultMallocZone(), args); ! 703: break; ! 704: default: ! 705: NX_ASSERT(FALSE, "Invalid term type in evalTerm"); ! 706: } ! 707: return result; ! 708: } ! 709: ! 710: /* Utility routine to look up a variable by name in the variable hashtable. */ ! 711: static Term *termOfVar(Expression *self, const char *varName) { ! 712: Term key; ! 713: ! 714: if (self->parseTree) { ! 715: key.tag = varTerm; ! 716: key.data.var.name = (char *)varName; ! 717: return NXHashGet(self->varTerms, &key); ! 718: } else ! 719: NX_RAISE(expErrNoText, self, NULL); ! 720: } ! 721: ! 722: /* adds a variable term to the Expressions hashtable of them */ ! 723: - (Term *)_addVarTerm:(const char *)name { ! 724: Term *newTerm; ! 725: Term *existingTerm; ! 726: ! 727: newTerm = _EXPAllocTerm([self zone], varTerm, 0); ! 728: newTerm->data.var.name = NXCopyStringBufferFromZone(name, [self zone]); ! 729: existingTerm = NXHashInsertIfAbsent(varTerms, newTerm); ! 730: NX_ASSERT(existingTerm == newTerm, "_addVarTerm: called with existing term"); ! 731: return newTerm; ! 732: } ! 733: ! 734: /* frees some storage, NULL'ing out the pointer */ ! 735: static void safeFree(void **data) { ! 736: free(*data); ! 737: *data = NULL; ! 738: } ! 739: ! 740: /* free function used in the NXHashTable prototype for functions */ ! 741: static void FunctionFree(const void *info, void *data) { ! 742: free(((Function *)data)->name); ! 743: free(data); ! 744: } ! 745: ! 746: /* ! 747: * Adds a func term to a HashTable of them. Returns any existing entry ! 748: * with the same name, else NULL. ! 749: */ ! 750: static Function *addFuncTerm(NXHashTable *table, const char *name, int min, int max, EXPTermEvalFunc *func) { ! 751: Function *newFunc; ! 752: Function *existingType; ! 753: ! 754: newFunc = NXZoneMalloc(NXZoneFromPtr(table), sizeof(Function)); ! 755: newFunc->name = NXCopyStringBufferFromZone(name, NXZoneFromPtr(table)); ! 756: newFunc->minArgs = min; ! 757: newFunc->maxArgs = max; ! 758: newFunc->evalFunc = func; ! 759: existingType = NXHashInsertIfAbsent(table, newFunc); ! 760: if (existingType != newFunc) ! 761: return existingType; ! 762: else ! 763: return NULL; ! 764: } ! 765: ! 766: /* ! 767: * Returns a global table of all built in functions. This table is shared ! 768: * by expressions that dont have application functions added to them. ! 769: */ ! 770: static NXHashTable *getBuiltInFuncs(void) { ! 771: if (!BuiltInFuncTable) ! 772: BuiltInFuncTable = makeBuiltInFuncTable(NXDefaultMallocZone()); ! 773: return BuiltInFuncTable; ! 774: } ! 775: ! 776: /* Returns a new hashtable of all built in functions. */ ! 777: static NXHashTable *makeBuiltInFuncTable(NXZone *zone) { ! 778: NXHashTable *table; ! 779: NXHashTablePrototype FuncTermProto; ! 780: const BuiltInFunc *bif; ! 781: int i; ! 782: ! 783: FuncTermProto = NXStrStructKeyPrototype; ! 784: FuncTermProto.free = FunctionFree; ! 785: table = NXCreateHashTableFromZone(FuncTermProto, NUM_BUILTIN_FUNCS, ! 786: NULL, zone); ! 787: for (i = NUM_BUILTIN_FUNCS, bif = FuncList; i--; bif++) ! 788: (void)addFuncTerm(table, bif->name, bif->minArgs, bif->maxArgs, bif->func); ! 789: return table; ! 790: } ! 791: ! 792: /* Returns the name of a term. */ ! 793: static char *termName(const Term *t) { ! 794: switch (t->tag) { ! 795: case varTerm: ! 796: return t->data.var.name; ! 797: case vectorTerm: ! 798: return t->data.vector.name; ! 799: default: ! 800: NX_ASSERT(FALSE, "Bogus term type in VarTermHash"); ! 801: return NULL; ! 802: } ! 803: } ! 804: ! 805: /* hashing function for variable terms. Used in hashtable prototypes. */ ! 806: static unsigned VarTermHash(const void *info, const void *data) { ! 807: return NXStrHash(info, termName(data)); ! 808: } ! 809: ! 810: /* comparison function for variable terms. Used in hashtable prototypes. */ ! 811: static int VarTermCompare(const void *info, const void *data1, const void *data2) { ! 812: return NXStrIsEqual(info, termName(data1), termName(data2)); ! 813: } ! 814: ! 815: @end ! 816: ! 817: /* These procs implement the built in functions */ ! 818: ! 819: static float sinStub(int numArgs, float *arg) { return sin(*arg); } ! 820: static float cosStub(int numArgs, float *arg) { return cos(*arg); } ! 821: static float tanStub(int numArgs, float *arg) { return tan(*arg); } ! 822: static float asinStub(int numArgs, float *arg) { return asin(*arg); } ! 823: static float acosStub(int numArgs, float *arg) { return acos(*arg); } ! 824: static float atanStub(int numArgs, float *arg) { return atan(*arg); } ! 825: static float expStub(int numArgs, float *arg) { return exp(*arg); } ! 826: static float lnStub(int numArgs, float *arg) { return log(*arg); } ! 827: static float sqrtStub(int numArgs, float *arg) { return sqrt(*arg); } ! 828: ! 829: static float sumStub(int numArgs, float *arg) { ! 830: float sum = 0.0; ! 831: ! 832: while (numArgs--) ! 833: sum += *arg++; ! 834: return sum; ! 835: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.