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