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