summaryrefslogtreecommitdiff
path: root/src/stable/formula.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/stable/formula.h808
1 files changed, 404 insertions, 404 deletions
diff --git a/src/stable/formula.h b/src/stable/formula.h
index 36a0398..b12afd6 100644
--- a/src/stable/formula.h
+++ b/src/stable/formula.h
@@ -21,410 +21,410 @@
21 21
22namespace Bu 22namespace Bu
23{ 23{
24 subExceptionDecl( FormulaException ); 24 subExceptionDecl( FormulaException );
25 /** 25 /**
26 * Implements a very simple formula parser that allows use of variables and 26 * Implements a very simple formula parser that allows use of variables and
27 * custom functions. This is based on a simple calculator-type parser that 27 * custom functions. This is based on a simple calculator-type parser that
28 * executes as it processes, accounting for operator precedence and 28 * executes as it processes, accounting for operator precedence and
29 * grouping. 29 * grouping.
30 * 30 *
31 * prec = precision, a type to use for all math (except binary ops) 31 * prec = precision, a type to use for all math (except binary ops)
32 * bin = binary type, a type to hard cast all data to for binary ops 32 * bin = binary type, a type to hard cast all data to for binary ops
33 */ 33 */
34 template<typename prec, typename bin=uint32_t> 34 template<typename prec, typename bin=uint32_t>
35 class Formula 35 class Formula
36 { 36 {
37 public: 37 public:
38 class Func 38 class Func
39 { 39 {
40 public: 40 public:
41 virtual prec operator()( prec )=0; 41 virtual prec operator()( prec )=0;
42 }; 42 };
43 43
44 typedef Hash<Bu::String, prec> varHash; 44 typedef Hash<Bu::String, prec> varHash;
45 typedef Hash<Bu::String, Func *> funcHash; 45 typedef Hash<Bu::String, Func *> funcHash;
46 46
47 Formula() 47 Formula()
48 { 48 {
49 } 49 }
50 50
51 virtual ~Formula() 51 virtual ~Formula()
52 { 52 {
53 for( typename funcHash::iterator i = hFunc.begin(); 53 for( typename funcHash::iterator i = hFunc.begin();
54 i != hFunc.end(); i++ ) 54 i != hFunc.end(); i++ )
55 { 55 {
56 delete (*i); 56 delete (*i);
57 } 57 }
58 } 58 }
59 59
60 prec run( const Bu::String &sFormulaSrc ) 60 prec run( const Bu::String &sFormulaSrc )
61 { 61 {
62 if( sFormulaSrc.isEmpty() ) 62 if( sFormulaSrc.isEmpty() )
63 throw FormulaException("Empty formula, nothing to do."); 63 throw FormulaException("Empty formula, nothing to do.");
64 try 64 try
65 { 65 {
66 const char *sFormula = sFormulaSrc.getStr(); 66 const char *sFormula = sFormulaSrc.getStr();
67 for(;;) 67 for(;;)
68 { 68 {
69 uint8_t tNum = nextToken( &sFormula ); 69 uint8_t tNum = nextToken( &sFormula );
70 if( tNum == symSubtract ) 70 if( tNum == symSubtract )
71 { 71 {
72 sOper.push( symNegate ); 72 sOper.push( symNegate );
73 continue; 73 continue;
74 } 74 }
75 else if( tNum == symNot ) 75 else if( tNum == symNot )
76 { 76 {
77 sOper.push( symNot ); 77 sOper.push( symNot );
78 continue; 78 continue;
79 } 79 }
80 else if( tNum == symOpenParen ) 80 else if( tNum == symOpenParen )
81 { 81 {
82 sOper.push( tNum ); 82 sOper.push( tNum );
83 continue; 83 continue;
84 } 84 }
85 else if( tNum == symFunction ) 85 else if( tNum == symFunction )
86 { 86 {
87 sOper.push( symFunction ); 87 sOper.push( symFunction );
88 continue; 88 continue;
89 } 89 }
90 else if( tNum == symEOS ) 90 else if( tNum == symEOS )
91 { 91 {
92 throw Bu::FormulaException( 92 throw Bu::FormulaException(
93 "Cannot end with an operator."); 93 "Cannot end with an operator.");
94 } 94 }
95 95
96 oppart: uint8_t tOpr = nextToken( &sFormula ); 96 oppart: uint8_t tOpr = nextToken( &sFormula );
97 if( tOpr == symEOS ) 97 if( tOpr == symEOS )
98 { 98 {
99 reduce(); 99 reduce();
100 prec ret = sValue.top(); 100 prec ret = sValue.top();
101 sValue.clear(); 101 sValue.clear();
102 sFunc.clear(); 102 sFunc.clear();
103 sOper.clear(); 103 sOper.clear();
104 return ret; 104 return ret;
105 } 105 }
106 if( !sOper.isEmpty() && getPrec( sOper.top() ) > 106 if( !sOper.isEmpty() && getPrec( sOper.top() ) >
107 getPrec( tOpr ) ) 107 getPrec( tOpr ) )
108 { 108 {
109 reduce(); 109 reduce();
110 } 110 }
111 if( tOpr != symCloseParen ) 111 if( tOpr != symCloseParen )
112 { 112 {
113 sOper.push( tOpr ); 113 sOper.push( tOpr );
114 } 114 }
115 else 115 else
116 { 116 {
117 reduce( true ); 117 reduce( true );
118 goto oppart; 118 goto oppart;
119 } 119 }
120 } 120 }
121 } 121 }
122 catch( ... ) 122 catch( ... )
123 { 123 {
124 sValue.clear(); 124 sValue.clear();
125 sFunc.clear(); 125 sFunc.clear();
126 sOper.clear(); 126 sOper.clear();
127 throw; 127 throw;
128 } 128 }
129 } 129 }
130 130
131 varHash hVars; 131 varHash hVars;
132 funcHash hFunc; 132 funcHash hFunc;
133 133
134 private: 134 private:
135 enum 135 enum
136 { 136 {
137 symEOS, 137 symEOS,
138 symAdd, 138 symAdd,
139 symSubtract, 139 symSubtract,
140 symMultiply, 140 symMultiply,
141 symDivide, 141 symDivide,
142 symOpenParen, 142 symOpenParen,
143 symCloseParen, 143 symCloseParen,
144 symNumber, 144 symNumber,
145 symVariable, 145 symVariable,
146 symFunction, 146 symFunction,
147 symExponent, 147 symExponent,
148 symNegate, 148 symNegate,
149 symModulus, 149 symModulus,
150 150
151 symAnd, 151 symAnd,
152 symOr, 152 symOr,
153 symXor, 153 symXor,
154 symNot 154 symNot
155 }; 155 };
156 156
157 typedef uint8_t symType; 157 typedef uint8_t symType;
158 158
159 Bu::Stack<symType> sOper; 159 Bu::Stack<symType> sOper;
160 Bu::Stack<prec> sValue; 160 Bu::Stack<prec> sValue;
161 Bu::Stack<Bu::String> sFunc; 161 Bu::Stack<Bu::String> sFunc;
162 162
163 private: 163 private:
164 symType getPrec( symType nOper ) 164 symType getPrec( symType nOper )
165 { 165 {
166 switch( nOper ) 166 switch( nOper )
167 { 167 {
168 case symNumber: 168 case symNumber:
169 case symVariable: 169 case symVariable:
170 case symOpenParen: 170 case symOpenParen:
171 case symCloseParen: 171 case symCloseParen:
172 return 0; 172 return 0;
173 173
174 case symAdd: 174 case symAdd:
175 case symSubtract: 175 case symSubtract:
176 return 1; 176 return 1;
177 177
178 case symMultiply: 178 case symMultiply:
179 case symDivide: 179 case symDivide:
180 case symModulus: 180 case symModulus:
181 return 2; 181 return 2;
182 182
183 case symAnd: 183 case symAnd:
184 case symOr: 184 case symOr:
185 case symXor: 185 case symXor:
186 return 2; 186 return 2;
187 187
188 case symExponent: 188 case symExponent:
189 case symNot: 189 case symNot:
190 case symNegate: 190 case symNegate:
191 case symFunction: 191 case symFunction:
192 return 3; 192 return 3;
193 193
194 default: 194 default:
195 return 0; 195 return 0;
196 } 196 }
197 } 197 }
198 198
199 symType nextToken( const char **sBuf ) 199 symType nextToken( const char **sBuf )
200 { 200 {
201 for(;;) 201 for(;;)
202 { 202 {
203 char cbuf = **sBuf; 203 char cbuf = **sBuf;
204 ++(*sBuf); 204 ++(*sBuf);
205 switch( cbuf ) 205 switch( cbuf )
206 { 206 {
207 case '+': 207 case '+':
208 return symAdd; 208 return symAdd;
209 209
210 case '-': 210 case '-':
211 return symSubtract; 211 return symSubtract;
212 212
213 case '*': 213 case '*':
214 return symMultiply; 214 return symMultiply;
215 215
216 case '/': 216 case '/':
217 return symDivide; 217 return symDivide;
218 218
219 case '^': 219 case '^':
220 return symExponent; 220 return symExponent;
221 221
222 case '%': 222 case '%':
223 return symModulus; 223 return symModulus;
224 224
225 case '(': 225 case '(':
226 return symOpenParen; 226 return symOpenParen;
227 227
228 case ')': 228 case ')':
229 return symCloseParen; 229 return symCloseParen;
230 230
231 case '|': 231 case '|':
232 return symOr; 232 return symOr;
233 233
234 case '&': 234 case '&':
235 return symAnd; 235 return symAnd;
236 236
237 case '#': 237 case '#':
238 return symXor; 238 return symXor;
239 239
240 case '~': 240 case '~':
241 return symNot; 241 return symNot;
242 242
243 case ' ': 243 case ' ':
244 case '\t': 244 case '\t':
245 case '\n': 245 case '\n':
246 case '\r': 246 case '\r':
247 break; 247 break;
248 248
249 case '\0': 249 case '\0':
250 return symEOS; 250 return symEOS;
251 251
252 default: 252 default:
253 if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) 253 if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') )
254 { 254 {
255 char num[50]={cbuf}; 255 char num[50]={cbuf};
256 int nPos = 1; 256 int nPos = 1;
257 bool bDot = false; 257 bool bDot = false;
258 258
259 for(;;) 259 for(;;)
260 { 260 {
261 cbuf = **sBuf; 261 cbuf = **sBuf;
262 if( cbuf == '.' ) 262 if( cbuf == '.' )
263 { 263 {
264 if( bDot == false ) 264 if( bDot == false )
265 bDot = true; 265 bDot = true;
266 else 266 else
267 throw FormulaException( 267 throw FormulaException(
268 "Numbers cannot have more than one " 268 "Numbers cannot have more than one "
269 ". in them." 269 ". in them."
270 ); 270 );
271 } 271 }
272 if( cbuf == '.' || 272 if( cbuf == '.' ||
273 (cbuf >= '0' && cbuf <= '9') ) 273 (cbuf >= '0' && cbuf <= '9') )
274 { 274 {
275 num[nPos++] = cbuf; 275 num[nPos++] = cbuf;
276 } 276 }
277 else 277 else
278 { 278 {
279 num[nPos] = '\0'; 279 num[nPos] = '\0';
280 sValue.push( 280 sValue.push(
281 static_cast<prec>( 281 static_cast<prec>(
282 strtod( num, NULL ) 282 strtod( num, NULL )
283 ) 283 )
284 ); 284 );
285 return symNumber; 285 return symNumber;
286 } 286 }
287 ++(*sBuf); 287 ++(*sBuf);
288 } 288 }
289 } 289 }
290 else if( (cbuf >= 'a' && cbuf <= 'z') || 290 else if( (cbuf >= 'a' && cbuf <= 'z') ||
291 (cbuf >= 'A' && cbuf <= 'Z') || 291 (cbuf >= 'A' && cbuf <= 'Z') ||
292 (cbuf == '_') ) 292 (cbuf == '_') )
293 { 293 {
294 char tok[50]={cbuf}; 294 char tok[50]={cbuf};
295 int nPos = 1; 295 int nPos = 1;
296 296
297 for(;;) 297 for(;;)
298 { 298 {
299 cbuf = **sBuf; 299 cbuf = **sBuf;
300 if( (cbuf >= 'a' && cbuf <= 'z') || 300 if( (cbuf >= 'a' && cbuf <= 'z') ||
301 (cbuf >= 'A' && cbuf <= 'Z') || 301 (cbuf >= 'A' && cbuf <= 'Z') ||
302 (cbuf >= '0' && cbuf <= '9') || 302 (cbuf >= '0' && cbuf <= '9') ||
303 cbuf == '_' || cbuf == '.' || cbuf == ':' ) 303 cbuf == '_' || cbuf == '.' || cbuf == ':' )
304 { 304 {
305 tok[nPos++] = cbuf; 305 tok[nPos++] = cbuf;
306 } 306 }
307 else 307 else
308 { 308 {
309 tok[nPos] = '\0'; 309 tok[nPos] = '\0';
310 if( hVars.has( tok ) ) 310 if( hVars.has( tok ) )
311 { 311 {
312 sValue.push( hVars[tok] ); 312 sValue.push( hVars[tok] );
313 return symNumber; 313 return symNumber;
314 } 314 }
315 else if( hFunc.has( tok ) ) 315 else if( hFunc.has( tok ) )
316 { 316 {
317 sFunc.push( tok ); 317 sFunc.push( tok );
318 return symFunction; 318 return symFunction;
319 } 319 }
320 else 320 else
321 { 321 {
322 throw FormulaException( 322 throw FormulaException(
323 "No variable or function named " 323 "No variable or function named "
324 "\"%s\" exists.", 324 "\"%s\" exists.",
325 tok 325 tok
326 ); 326 );
327 } 327 }
328 } 328 }
329 ++(*sBuf); 329 ++(*sBuf);
330 } 330 }
331 } 331 }
332 break; 332 break;
333 } 333 }
334 } 334 }
335 } 335 }
336 336
337 void reduce( bool bCloseParen = false ) 337 void reduce( bool bCloseParen = false )
338 { 338 {
339 while( !sOper.isEmpty() ) 339 while( !sOper.isEmpty() )
340 { 340 {
341 uint8_t nOpr = sOper.top(); 341 uint8_t nOpr = sOper.top();
342 if( nOpr == symOpenParen ) 342 if( nOpr == symOpenParen )
343 { 343 {
344 if( bCloseParen == true ) 344 if( bCloseParen == true )
345 sOper.pop(); 345 sOper.pop();
346 return; 346 return;
347 } 347 }
348 sOper.pop(); 348 sOper.pop();
349 349
350 prec dTop = sValue.top(); 350 prec dTop = sValue.top();
351 sValue.pop(); 351 sValue.pop();
352 352
353 switch( nOpr ) 353 switch( nOpr )
354 { 354 {
355 case symAdd: 355 case symAdd:
356 sValue.top() += dTop; 356 sValue.top() += dTop;
357 break; 357 break;
358 358
359 case symSubtract: 359 case symSubtract:
360 sValue.top() -= dTop; 360 sValue.top() -= dTop;
361 break; 361 break;
362 362
363 case symMultiply: 363 case symMultiply:
364 sValue.top() *= dTop; 364 sValue.top() *= dTop;
365 break; 365 break;
366 366
367 case symDivide: 367 case symDivide:
368 sValue.top() /= dTop; 368 sValue.top() /= dTop;
369 break; 369 break;
370 370
371 case symExponent: 371 case symExponent:
372 sValue.top() = static_cast<prec>( 372 sValue.top() = static_cast<prec>(
373 pow( sValue.top(), dTop ) 373 pow( sValue.top(), dTop )
374 ); 374 );
375 break; 375 break;
376 376
377 case symModulus: 377 case symModulus:
378 sValue.top() = static_cast<prec>( 378 sValue.top() = static_cast<prec>(
379 fmod( sValue.top(), dTop ) 379 fmod( sValue.top(), dTop )
380 ); 380 );
381 break; 381 break;
382 382
383 case symOr: 383 case symOr:
384 sValue.top() = static_cast<prec>( 384 sValue.top() = static_cast<prec>(
385 static_cast<bin>(sValue.top()) | 385 static_cast<bin>(sValue.top()) |
386 static_cast<bin>(dTop) 386 static_cast<bin>(dTop)
387 ); 387 );
388 break; 388 break;
389 389
390 case symAnd: 390 case symAnd:
391 sValue.top() = static_cast<prec>( 391 sValue.top() = static_cast<prec>(
392 static_cast<bin>(sValue.top()) & 392 static_cast<bin>(sValue.top()) &
393 static_cast<bin>(dTop) 393 static_cast<bin>(dTop)
394 ); 394 );
395 break; 395 break;
396 396
397 case symXor: 397 case symXor:
398 sValue.top() = static_cast<prec>( 398 sValue.top() = static_cast<prec>(
399 static_cast<bin>(sValue.top()) ^ 399 static_cast<bin>(sValue.top()) ^
400 static_cast<bin>(dTop) 400 static_cast<bin>(dTop)
401 ); 401 );
402 break; 402 break;
403 403
404 case symFunction: 404 case symFunction:
405 sValue.push( (*hFunc.get( sFunc.pop() ))( dTop ) ); 405 sValue.push( (*hFunc.get( sFunc.pop() ))( dTop ) );
406 break; 406 break;
407 407
408 case symNegate: 408 case symNegate:
409 sValue.push( -dTop ); 409 sValue.push( -dTop );
410 break; 410 break;
411 411
412 case symNot: 412 case symNot:
413 sValue.push( static_cast<prec>( 413 sValue.push( static_cast<prec>(
414 ~static_cast<bin>(dTop) 414 ~static_cast<bin>(dTop)
415 ) ); 415 ) );
416 break; 416 break;
417 } 417 }
418 } 418 }
419 419
420 if( bCloseParen == true ) 420 if( bCloseParen == true )
421 { 421 {
422 throw FormulaException( 422 throw FormulaException(
423 "Close-paren found without matching open-paren." 423 "Close-paren found without matching open-paren."
424 ); 424 );
425 } 425 }
426 } 426 }
427 }; 427 };
428} 428}
429 429
430#endif 430#endif