1 /**
2 * @namespace biew_addons
3 * @file addons/tools/eval.c
4 * @brief A simple mathematical expression evaluator in C.
5 * @version -
6 * @remark This subset version is hereby donated to the public domain.
7 * @note Requires POSIX compatible development system
8 *
9 * @author Original Copyright 1991-93 by Robert B. Stout as part of
10 * the MicroFirm Function Library (MFL)
11 * @since 1991
12 * @author Nickols_K
13 * @date 2000
14 * @note Modified for using with BIEW, remove all floating point part.
15 **/
16 /************************************************************************/
17 /* */
18 /* EVALUATE.C - A simple mathematical expression evaluator in C */
19 /* */
20 /* operators supported: Operator Precedence */
21 /* */
22 /* ( Lowest */
23 /* ) Highest */
24 /* + (addition) Low */
25 /* - (subtraction) Low */
26 /* * (multiplication) Medium */
27 /* / (division) Medium */
28 /* % (modulus) High */
29 /* Added by Nickols_K */
30 /* << (left shift) Low */
31 /* >> (right shift) Low */
32 /* & (bitwise and) Low */
33 /* | (bitwise or) Low */
34 /* ^ (bitwise xor) Low */
35 /* ~ (bitwise 1's complement) Low */
36 /* */
37 /* Original Copyright 1991-93 by Robert B. Stout as part of */
38 /* the MicroFirm Function Library (MFL) */
39 /* */
40 /* This subset version is hereby donated to the public domain. */
41 /* Requires RMALLWS.C, also in SNIPPETS. */
42 /* */
43 /************************************************************************/
44 #include <string.h>
45 #include <stdlib.h>
46 #include <ctype.h>
47 #include <math.h>
48 #include <stdio.h>
49
50 #include "colorset.h"
51 #include "reg_form.h"
52 #include "biewutil.h"
53 #include "bconsole.h"
54 #include "biewlib/biewlib.h"
55 #include "biewlib/kbd_code.h"
56 #include "biewlib/pmalloc.h"
57
58
59 #define LAST_CHAR(string) (((char *)string)[strlen(string)-1])
60
61 /*
62 This struct is ordered as it documented in Athlon manual
63 Publication # 22007 Rev: D
64 */
65 struct operator {
66 const char *tag;
67 size_t taglen;
68 int precedence;
69 const char token;
70 };
71
72 #define NUL '\0'
73
74
75 typedef enum { E_MEM = -4,
76 O_ERROR = -3,
77 R_ERROR = -2, /** range */
78 S_ERROR, /** syntax */
79 SUCCESS} STATUS;
80
81 static struct operator verbs[] = {
82 {"+", 1, 2, '+' },
83 {"-", 1, 3, '-' },
84 {"*", 1, 4, '*' },
85 {"/", 1, 5, '/' },
86 {"%", 1, 5, '%' },
87 {"<<",2, 1, '<' },
88 {">>",2, 1, '>' },
89 {"&", 1, 1, '&' },
90 {"^", 1, 1, '^' },
91 {"|", 1, 1, '|' },
92 {"~", 1, 1, '~' },
93 {"(", 1, 0, '(' },
94 {")", 1, 99,')' },
95 {NULL,0, 0, NUL }
96 };
97
98 #define EVAL_STACK_SIZE 256
99
100 static char *op_stack; /** Operator stack */
101 static tIntMax*arg_stack; /** Argument stack */
102 static char *token; /** Token buffer */
103 static int op_sptr, /** op_stack pointer */
104 arg_sptr, /** arg_stack pointer */
105 parens, /** Nesting level */
106 state; /** 0 = Awaiting expression
107 1 = Awaiting operator
108 */
109 int evaluate(char *, tIntMax *,int *);
110
111 /*
112 ** Originally published as part of the MicroFirm Function Library
113 **
114 ** Copyright 1986, S.E. Margison
115 ** Copyright 1989, Robert B.Stout
116 **
117 ** Subset version released to the public domain, 1991
118 **
119 ** remove all whitespace from a string
120 */
121
122 /** Modified for using with BIEW by Nickols_K (2000) */
123
rmallws(char * str)124 static char * __NEAR__ __FASTCALL__ rmallws(char *str)
125 {
126 char *obuf, *nbuf;
127
128 for (obuf = str, nbuf = str; *obuf && obuf; ++obuf)
129 {
130 if (!isspace((unsigned char)*obuf))
131 *nbuf++ = *obuf;
132 }
133 *nbuf = NUL;
134 return str;
135 }
136
137 static int __NEAR__ __FASTCALL__ do_op(void);
138 static int __NEAR__ __FASTCALL__ do_paren(void);
139 static void __NEAR__ __FASTCALL__ push_op(char);
140 static void __NEAR__ __FASTCALL__ push_arg(tIntMax);
141 static int __NEAR__ __FASTCALL__ pop_arg(tIntMax *);
142 static int __NEAR__ __FASTCALL__ pop_op(int *);
143 static char *__NEAR__ __FASTCALL__ get_exp(char *);
144 static struct operator * __NEAR__ __FASTCALL__ get_op(char *);
145 static int __NEAR__ __FASTCALL__ getprec(char);
146 static int __NEAR__ __FASTCALL__ getTOSprec(void);
147
148 #ifdef TEST
149
150 #include <stdio.h>
151
main(int argc,char * argv[])152 int main(int argc, char *argv[])
153 {
154 int retval,base;
155 tIntMax val;
156 char sout[100];
157 if(argc < 2)
158 {
159 printf("Usage : eval <expr>\n");
160 return 0;
161 }
162 printf("evaluate(%s)\n", argv[1]);
163 retval = evaluate(argv[1], &val, &base);
164 sout[0] = 0;
165 switch(base)
166 {
167 case 16: strcpy(sout,"0x"); break;
168 case 8: strcpy(sout,"0"); break;
169 default: break;
170 }
171 ltoa(val,&sout[strlen(sout)],base);
172 switch(base)
173 {
174 case 2: strcat(sout,"b"); break;
175 default: break;
176 }
177 if (retval == 0) printf("%s\n",sout);
178 else printf(retval == S_ERROR ? "syntax error\n" :
179 retval == R_ERROR ? "runtime error\n":
180 retval == O_ERROR ? "bad operand\n":
181 "out of memory\n");
182 return 0;
183 }
184
185 #endif
186
187 /************************************************************************/
188 /* */
189 /* evaluate() */
190 /* */
191 /* Evaluates an ASCII mathematical expression. */
192 /* */
193 /* Arguments: 1 - String to evaluate */
194 /* 2 - Storage to receive double result */
195 /* */
196 /* Returns: SUCCESS if successful */
197 /* S_ERROR if syntax error */
198 /* R_ERROR if runtime error */
199 /* O_ERROR if bad operand */
200 /* E_MEM if no memory */
201 /* */
202 /* Side effects: Removes all whitespace from the string and converts */
203 /* it to U.C. */
204 /* */
205 /************************************************************************/
206
evaluate(char * line,tIntMax * val,int * result_base)207 int evaluate(char *line, tIntMax *val,int *result_base)
208 {
209 tIntMax arg;
210 char *ptr = line, *str, *endptr;
211 int ercode;
212 int retval;
213 struct operator *op;
214 op_stack = PMalloc(EVAL_STACK_SIZE*sizeof(char));
215 arg_stack = PMalloc(EVAL_STACK_SIZE*sizeof(tIntMax));
216 token = PMalloc(EVAL_STACK_SIZE*sizeof(char));
217 if((!op_stack) || (!arg_stack) || (!token))
218 {
219 if(op_stack) PFREE(op_stack);
220 if(arg_stack) PFREE(arg_stack);
221 if(token) PFREE(token);
222 return E_MEM;
223 }
224 strupr(line);
225 rmallws(line);
226 *result_base = 0;
227 state = op_sptr = arg_sptr = parens = 0;
228 retval = SUCCESS;
229 while (*ptr)
230 {
231 switch (state)
232 {
233 case 0:
234 if (NULL != (str = get_exp(ptr)))
235 {
236 if (NULL != (op = get_op(str)) &&
237 strlen(str) == op->taglen)
238 {
239 push_op(op->token);
240 ptr += op->taglen;
241 break;
242 }
243
244 if (SUCCESS == strcmp(str, "-"))
245 {
246 push_op(*str);
247 ++ptr;
248 break;
249 }
250
251 else
252 {
253 /** determine type of number */
254 int base;
255 char num[50];
256 char *e_num,*end;
257 char un_op = 0;
258 memcpy(num,str,sizeof(num));
259 num[49] = 0;
260 e_num = num;
261 if(*e_num == '~') { un_op = *e_num; e_num++; }
262 end = &e_num[strlen(e_num)-1];
263 if(e_num[0] == '0')
264 {
265 if(e_num[1] == 'x' || e_num[1] == 'X')
266 {
267 e_num = &e_num[2];
268 base = 16;
269 }
270 else
271 {
272 e_num = &e_num[1];
273 base = 8;
274 }
275 }
276 else
277 if(*end == 'b' || *end == 'B')
278 {
279 *end = 0;
280 base = 2;
281 }
282 else base = 10;
283 #if (__WORDSIZE >= 32) && !defined(__QNX4__)
284 if (0 == (arg = strtoll(e_num, &endptr,base)) &&
285 #else
286 if (0 == (arg = strtol(e_num, &endptr,base)) &&
287 #endif
288 NULL == strchr(num, '0'))
289 {
290 retval = O_ERROR;
291 goto exit;
292 }
293 if(endptr != &e_num[strlen(e_num)])
294 {
295 retval = O_ERROR;
296 goto exit;
297 }
298 if(!(*result_base)) *result_base = base;
299 switch(un_op)
300 {
301 default:
302 case 0: push_arg(arg); break;
303 case '~': push_arg(~arg); break;
304 }
305 }
306 ptr += strlen(str);
307 }
308 else
309 {
310 retval = S_ERROR;
311 goto exit;
312 }
313 state = 1;
314 break;
315
316 case 1:
317 if (NULL != (op = get_op(ptr)))
318 {
319 if (')' == *ptr)
320 {
321 if (SUCCESS > (ercode = do_paren()))
322 {
323 retval = ercode;
324 goto exit;
325 }
326 }
327 else
328 {
329 while (op_sptr &&
330 op->precedence <= getTOSprec())
331 {
332 do_op();
333 }
334 push_op(op->token);
335 state = 0;
336 }
337
338 ptr += op->taglen;
339 }
340 else
341 {
342 retval = S_ERROR;
343 goto exit;
344 }
345 break;
346 }
347 }
348
349 while (1 < arg_sptr)
350 {
351 if (SUCCESS > (ercode = do_op()))
352 {
353 retval = ercode;
354 goto exit;
355 }
356 }
357 if (!op_sptr)
358 {
359 retval = pop_arg(val);
360 goto exit;
361 }
362 else
363 {
364 retval = S_ERROR;
365 goto exit;
366 }
367 exit:
368 PFREE(op_stack);
369 PFREE(arg_stack);
370 PFREE(token);
371 return retval;
372 }
373
374 /**
375 ** Evaluate stacked arguments and operands
376 */
377
do_op(void)378 static int __NEAR__ __FASTCALL__ do_op(void)
379 {
380 tIntMax arg1, arg2;
381 int op;
382
383 if (S_ERROR == pop_op(&op))
384 return S_ERROR;
385
386 pop_arg(&arg1);
387 pop_arg(&arg2);
388
389 switch (op)
390 {
391 case '+':
392 push_arg(arg2 + arg1);
393 break;
394
395 case '-':
396 push_arg(arg2 - arg1);
397 break;
398
399 case '*':
400 push_arg(arg2 * arg1);
401 break;
402
403 case '/':
404 if (0 == arg1)
405 return R_ERROR;
406 push_arg(arg2 / arg1);
407 break;
408
409 case '%':
410 if (0 == arg1)
411 return R_ERROR;
412 push_arg(arg2 % arg1);
413 break;
414
415 case '(':
416 arg_sptr += 2;
417 break;
418
419 case '<':
420 push_arg(arg2 << arg1);
421 break;
422 case '>':
423 push_arg(arg2 >> arg1);
424 break;
425 case '&':
426 push_arg(arg2 & arg1);
427 break;
428 case '^':
429 push_arg(arg2 ^ arg1);
430 break;
431 case '|':
432 push_arg(arg2 | arg1);
433 break;
434 case '~':
435 push_arg(~arg2);
436 break;
437 default:
438 return S_ERROR;
439 }
440 return 1 > arg_sptr ? S_ERROR : op;
441 }
442
443 /**
444 ** Evaluate one level
445 */
446
do_paren(void)447 static int __NEAR__ __FASTCALL__ do_paren(void)
448 {
449 int op;
450
451 if (1 > parens--)
452 return S_ERROR;
453 do
454 {
455 if (SUCCESS > (op = do_op()))
456 break;
457 } while (getprec((char)op));
458 return op;
459 }
460
461 /**
462 ** Stack operations
463 */
464
push_op(char op)465 static void __NEAR__ __FASTCALL__ push_op(char op)
466 {
467 if (!getprec(op))
468 ++parens;
469 op_stack[op_sptr++] = op;
470 }
471
push_arg(tIntMax arg)472 static void __NEAR__ __FASTCALL__ push_arg(tIntMax arg)
473 {
474 arg_stack[arg_sptr++] = arg;
475 }
476
pop_arg(tIntMax * arg)477 static int __NEAR__ __FASTCALL__ pop_arg(tIntMax *arg)
478 {
479 *arg = arg_stack[--arg_sptr];
480 return 0 > arg_sptr ? S_ERROR : SUCCESS;
481 }
482
pop_op(int * op)483 static int __NEAR__ __FASTCALL__ pop_op(int *op)
484 {
485 if (!op_sptr)
486 return S_ERROR;
487 *op = op_stack[--op_sptr];
488 return SUCCESS;
489 }
490
491 /**
492 ** Get an expression
493 */
494
get_exp(char * str)495 static char * __NEAR__ __FASTCALL__ get_exp(char *str)
496 {
497 char *ptr = str, *tptr = token;
498 struct operator *op;
499
500 while (*ptr)
501 {
502 if (NULL != (op = get_op(ptr)))
503 {
504 if ('-' == *ptr || '+' == *ptr || '~' == *ptr)
505 {
506 if (str != ptr)
507 break;
508 if (str == ptr && !isdigit((unsigned char)ptr[1]))
509 {
510 push_arg(0);
511 strcpy(token, op->tag);
512 return token;
513 }
514 }
515
516 else if (str == ptr)
517 {
518 strcpy(token, op->tag);
519 return token;
520 }
521
522 else break;
523 }
524
525 *tptr++ = *ptr++;
526 }
527 *tptr = NUL;
528
529 return token;
530 }
531
532 /**
533 ** Get an operator
534 */
535
get_op(char * str)536 static struct operator * __NEAR__ __FASTCALL__ get_op(char *str)
537 {
538 struct operator *op;
539
540 for (op = verbs; op->token; ++op)
541 {
542 if (SUCCESS == strncmp(str, op->tag, op->taglen))
543 return op;
544 }
545 return NULL;
546 }
547
548 /**
549 ** Get precedence of a token
550 */
551
getprec(char _token)552 static int __NEAR__ __FASTCALL__ getprec(char _token)
553 {
554 struct operator *op;
555
556 for (op = verbs; op->token; ++op)
557 {
558 if (_token == op->token)
559 break;
560 }
561 return op->token ? op->precedence : 0;
562 }
563
564 /**
565 ** Get precedence of TOS token
566 */
567
getTOSprec(void)568 static int __NEAR__ __FASTCALL__ getTOSprec(void)
569 {
570 if (!op_sptr)
571 return 0;
572 return getprec(op_stack[op_sptr - 1]);
573 }
574
CalculatorFunc(void)575 static void CalculatorFunc(void)
576 {
577 tAbsCoord x1_,y1_,x2_,y2_;
578 tRelCoord X1,Y1,X2,Y2;
579 int ret;
580 TWindow * wdlg,*ewnd;
581 char estr[81];
582 wdlg = CrtDlgWndnls(" Calculator ",78,7);
583 twGetWinPos(wdlg,&x1_,&y1_,&x2_,&y2_);
584 X1 = x1_;
585 Y1 = y1_;
586 X2 = x2_;
587 Y2 = y2_;
588 X1 += 2;
589 X2 -= 1;
590 Y1 += 2;
591 Y2 = Y1;
592 ewnd = WindowOpen(X1,Y1,X2,Y2,TWS_VISIBLE | TWS_CURSORABLE | TWS_NLSOEM);
593 twSetColorAttr(dialog_cset.editor.active);
594 twUseWin(wdlg);
595 twGotoXY(2,1); twPutS("Input an integer expression :");
596 twinDrawFrameAttr(1,3,78,7,TW_UP3D_FRAME,dialog_cset.main);
597 twGotoXY(2,4); twPutS("Supported operators: + - * / ( ) % << >> & ^ | ~");
598 twGotoXY(2,5); twPutS("Supported bases: 0x - hexadecimal, 0 - octal, 1b - binary, default - decimal");
599 twGotoXY(2,6); twPutS("Result has the base of the first operand");
600 memset(estr,0,sizeof(estr));
601 twShowWin(ewnd);
602 twUseWin(ewnd);
603 while(1)
604 {
605 ret = xeditstring(estr,NULL,76,NULL);
606 if(ret == KE_ESCAPE || ret == KE_F(10)) break;
607 else
608 if(ret == KE_ENTER)
609 {
610 tIntMax val;
611 int _ret,base;
612 _ret = evaluate(estr,&val,&base);
613 if(_ret != SUCCESS)
614 {
615 ErrMessageBox(_ret == O_ERROR ? "Bad operand" :
616 _ret == R_ERROR ? "Runtime error" :
617 _ret == E_MEM ? "Not enough memory!" :
618 "Syntax error",NULL);
619 }
620 else
621 {
622 char sres[80];
623 sres[0] = 0;
624 switch(base)
625 {
626 case 16: strcpy(sres,"0x"); break;
627 case 8: strcpy(sres,"0"); break;
628 default: break;
629 }
630 #if (__WORDSIZE >= 32) && !defined(__QNX4__)
631 lltoa(val,&sres[strlen(sres)],base);
632 #else
633 ltoa(val,&sres[strlen(sres)],base);
634 #endif
635 switch(base)
636 {
637 case 2: strcat(sres,"b"); break;
638 default: break;
639 }
640 NotifyBox(sres," Result ");
641 }
642 continue;
643 }
644 }
645 CloseWnd(ewnd);
646 CloseWnd(wdlg);
647 }
648
649
650 REGISTRY_TOOL Calculator =
651 {
652 "~String calculator",
653 CalculatorFunc,
654 NULL,
655 NULL
656 };
657
658
659
660
661