diff options
Diffstat (limited to 'src/stable/formula.h')
-rw-r--r-- | src/stable/formula.h | 808 |
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 | ||
22 | namespace Bu | 22 | namespace 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 |