|
|
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.