|
|
1.1 root 1:
2: #import <objc/Object.h>
3: #import <objc/hashtable.h>
4: #import <appkit/errors.h>
5:
6: /*
7: * An Expression object parses and evaluates the text of a mathematical
8: * expression. The expression text may contain numbers, variables,
9: * arithmetic operations and program defined functions. For example,
10: * an Expression object can parse the string "a+b*c", and if told
11: * values for a, b and c, can calculate the value of the expression.
12: *
13: * This ability to parse and evaluate expressions at runtime makes it
14: * easy for simple mathmatical programs to move beyond having canned example
15: * functions compiled into their executables, and instead allow the user
16: * to enter novel equations. New formulas can be tried without recompiling
17: * the application.
18: *
19: * Typically an Expression is created and then told to parse some text
20: * entered by the user. The result of the parse is a parse tree, which is
21: * then used by the Expression to evaluate the expression, given a set
22: * of values for the expression's variables. Variables can have either a
23: * single value, or can be made to take on a series of values ("vector
24: * variables"). For example, if you were graphing "A*x^2", you might
25: * make A have a single value, but let x run over a range of
26: * values that you would like to plot.
27: *
28: * The values of these vector variables can be set in two ways. In the
29: * first way, a list of values is passed in using the setVar:vector:numVals:
30: * method. In the second way, the variable is given a range for its
31: * values with the setVar:min:max: method. The actual values are then
32: * interpolated within that range. The resolution of the expression
33: * determines how many values are calculated. If you mix these two styles
34: * of vector variables, you must ensure that the number of explicitly
35: * set values assigned to any variables matches the resolution of the
36: * Expression.
37: *
38: * If there are more than one range-style vector variables in an Expression
39: * they may be interpolated together or orthogonally, creating
40: * multi-dimensional domains. You can set the total number of dimensions over
41: * which the expression is evaluated, and then set the dimension of each
42: * vector variable that is being interpolated.
43: *
44: * Expressions always operate lazily, meaning that results are never
45: * calculated until they are needed (usually when result values are asked
46: * for). This means just changing the values of variables is inexpensive.
47: *
48: * Expressions have methods which allow an application to enumerate the
49: * names of all the variables found by the parse. This can be used to verify
50: * that the expression is valid, beyond whether it was parsable. For
51: * example, in a certain context there may be a fixed set of variable names
52: * that may be used. After a successful parse, the application can run
53: * through the names of all variables found, and ensure that they are all
54: * appropriate.
55: *
56: * Expressions understand the arithmetic operators +, -, *, /. % is used
57: * for modulus (as in C) and ^ means is used to raise a quantity to a power.
58: * Parentheses can be used for grouping.
59: *
60: * Expressions have certain "built in" functions (e.g., sin()) that are
61: * understood. It is also possible for applications to extend this default
62: * set of functions. New functions are registered with the name of the
63: * function, the allowable number of arguments (can be variable), and a C
64: * procedure to call to perform the evaluation. The built in functions are:
65: *
66: * sin(x), cos(x), tan(x) - elementary trig
67: * asin(x), acos(x), atan(x) - inverse elementary trig
68: * exp(x), ln(x) - exponential and natural log
69: * sqrt(x) - square root
70: *
71: * The constants "pi" and "e" are also built in.
72: */
73:
74: /* function supplied by term implementor for evaluation */
75: typedef float EXPTermEvalFunc(int numArgs, float *args);
76:
77: /* enumeration state used to loop through all the variable names */
78: typedef void *EXPEnumState;
79:
80: /* private type for representing terms */
81: typedef struct _EXPTerm *EXPTermPtr;
82:
83: @interface Expression : Object {
84: char *text; /* text of the expression */
85: NXHashTable *varTerms; /* terms of variables */
86: EXPTermPtr parseTree; /* terms from the parse */
87: NXHashTable *validFuncs; /* functions we know how to evaluate */
88: int resolution; /* number of points to calc for range vars */
89: BOOL resultsValid; /* are the results up to date? */
90: short dimensions; /* #axes of evaluation, defaults to 1 */
91: float *results; /* results of evaluation */
92: float resultsMin; /* min of all results */
93: float resultsMax; /* max of all results */
94: }
95:
96: - init;
97: /*
98: * Initialize an Expression that was just created via "allocFromZone:". You
99: * cannot use a "+new" method to create Expression objects. Below are some
100: * examples of creating Expressions. The first expression goes in the
101: * default malloc zone, the second is allocated in the same zone as
102: * otherObject.
103: *
104: * id myExp1, myExp2;
105: * myExp1 = [[Expression alloc] init];
106: * myExp2 = [[Expression allocFromZone:[otherObject zone]] init];
107: *
108: */
109:
110: - free;
111: /*
112: * Frees the Expression, including any array of results returned by
113: * the resultsVector:numVals: method.
114: */
115:
116: - (BOOL)parse:(const char *)expressionString;
117: /*
118: * Parses the text of an expression. A parse tree of terms is built up as
119: * a result of parsing expressionString. The method returns whether the
120: * string was a legal expression. expressionString is copied and retained
121: * within the Expression.
122: */
123:
124: - (const char *)text;
125: /*
126: * Returns the last text parsed by the Expression.
127: */
128:
129: - setResolution:(int)count;
130: /*
131: * Sets the resolution at which variables with a min and max range will
132: * be subdivided. All vectors in the Expression must have the same
133: * number of values, which must be equal to the resolution of the
134: * Expression, at the time the Expression is evaluated. Note that setting
135: * the list of values of a vector variable also changes the Expression's
136: * resolution.
137: */
138:
139: - (int)resolution;
140: /*
141: * Returns the resolution of the Expression.
142: */
143:
144: - setVar:(const char *)varName value:(float)val;
145: /*
146: * Sets the value of the variable named varName to val. The variable
147: * will have that value as a constant throughout subsequent evaluations.
148: */
149:
150: - (float)varValue:(const char *)varName;
151: /*
152: * Returns the value of the variable varName. If the variable is being
153: * used as a vector, then its first value is returned.
154: */
155:
156: - setVar:(const char *)varName vector:(float *)vals numVals:(int)count;
157: /*
158: * Sets the values of the variable named varName to be the array
159: * vals. Count is the number of values in the vector. This method
160: * also sets the resolution of the Expression to be count.
161: * All vectors in the Expression must have the same number of values,
162: * which must be equal to the resolution of the Expression, at the
163: * time the Expression is evaluated. The list of vals should be a block
164: * of floats returned from malloc. It is NOT copied, but will be freed by
165: * the Expression as part of its own free method.
166: */
167:
168: - varVector:(const char *)varName vector:(float **)vals numVals:(int *)count;
169: /*
170: * Returns the vector of values of the variable varName by setting
171: * vals to point to the vector. Count is set to the number of values.
172: */
173:
174: - setVar:(const char *)varName min:(float)minVal max:(float)maxVal;
175: /*
176: * Sets the range of the variable varName to run from minVal to maxVal.
177: * The variables values will be determined by interpolating points
178: * within this range. The resolution of the Expression determines the
179: * number of points that are taken within the range.
180: */
181:
182: - setVar:(const char *)varName dimension:(short)dimensionNum;
183: /*
184: * Sets the dimension within which the variable will vary. The given value
185: * must be between 0 and [expression dimensions]-1.
186: */
187:
188: - var:(const char *)varName dimension:(short *)dimensionNum;
189: /*
190: * Returns the dimension within which the variable will vary.
191: */
192:
193: - var:(const char *)varName min:(float *)minVal max:(float *)maxVal;
194: /*
195: * Returns the smallest and largest value of the variable varName by setting
196: * minVal and maxVal.
197: */
198:
199: - (float)resultValue;
200: /*
201: * Returns the value of the Expression when evaluated with its current
202: * attributes. If there are vector variables in the Expression, it
203: * returns the result using the first value of all vectors.
204: */
205:
206: - resultsVector:(float **)vals numVals:(int *)count;
207: /*
208: * Returns the values of the Expression when evaluated with its current
209: * attributes, by setting vals to point to the vector of results. count
210: * is set to the number of results. The number of results returned will be
211: * resolution^dimensions. If there are no vector variables in the
212: * Expression, a single result is returned.
213: */
214:
215: - resultsMin:(float *)minVal max:(float *)maxVal;
216: /*
217: * Returns the smallest and largest value of the results by setting
218: * minVal and maxVal.
219: */
220:
221: - setDimensions:(short)count;
222: /*
223: * Sets the number of evaluation dimensions. The number of values in the
224: * results vector is resolution^dimensions. A given variable varies in one
225: * dimension, as set by setVar:dimension:.
226: */
227:
228: - (short)dimensions;
229: /*
230: * Returns the number of evaluation dimensions.
231: */
232:
233: - (EXPEnumState)beginVariableEnumeration;
234: - (const char *)nextVariable:(EXPEnumState)state;
235: - (void)endVariableEnumeration:(EXPEnumState)state;
236: /*
237: * Used to walk through the names of all variables parsed. Example:
238: * EXPEnumState state = [myExp beginVariableEnumeration];
239: * const char *varName;
240: * while (varName = [myExp nextVariable:state])
241: * printf("A variable named %s was parsed.\n", varName);
242: * [myExp endVariableEnumeration:state];
243: */
244:
245: - addFuncTerm:(const char *)name minArgs:(int)min maxArgs:(int)max
246: evalFunc:(EXPTermEvalFunc *)func;
247: /*
248: * Adds a function to the set of functions this Expression can parse.
249: * Functions look like "name(arg1, arg2,...)" in the text that is
250: * parsed. At evaluation time the C function func() will be called with
251: * the values of the arguments. The arguments are passed in an array of
252: * floats(see the EXPTermEvalFunc typedef above). Func must return the value
253: * of the function with those arguments. min and max determine how many
254: * arguments the function can accept. For example, min=1, max=1 means
255: * the function takes one argument. A max value of -1 means an unbounded
256: * number of arguments is allowed.
257: */
258:
259: - removeFuncTerm:(const char *)name;
260: /*
261: * Removes a function from the set of functions this Expression can parse.
262: * Be careful not to send this to an Expression that has already parsed
263: * text which made use of this function, since a reference to this FuncTerm
264: * will be left dangling in the parse tree.
265: */
266:
267: @end
268:
269: /* Error codes we raise */
270:
271: typedef enum {
272: expErrInvalidVarName = NX_APPBASE + 10000,
273: /* An argument was passed referring to a variable whose name did not exist in the expression parsed. */
274: expErrInvalidVarType,
275: /* A variable was used in a way inconsistent with its type. */
276: expErrMinMax,
277: /* A min parameter was not less that its accompanying max parameter. */
278: expErrNoText,
279: /* A method could not complete because no expression had been parsed. */
280: expErrResolutionMismatch,
281: /* The number of points in the vector terms and the resolution of the Expression are inconsistent. */
282: expFuncTypeInUse,
283: /* A message was sent attempting to add a function which has already been declared. */
284: expInvalidDimension
285: /* A invalid value for a var's dimension was passed. */
286: } EXPError;
287:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.