Annotation of Examples/AppKit/Graph/Expression.m, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.