Advertisement
Guest User

C++ Calculator using Shunting Yard Algorithm

a guest
Aug 16th, 2017
245
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.01 KB | None | 0 0
  1. #if 0
  2. g++ -o ${0:0:${#0}-4} $0; exit
  3. #else
  4. /*---------------------------------------------------------------*\
  5. | Basic Calculator using Shunting Yard Algorithm       <calc.cpp> |
  6. |                                                                 |
  7. +-----------------------------------------------------------------+
  8. | This program is free software; you can redistribute it and/or   |
  9. | modify it under the terms of the GNU General Public License as  |
  10. | published by the Free Software Foundation; either version 3 of, |
  11. | or the License (at your option) any later version.              |
  12. |                                                                 |
  13. | This program is distributed in the hope that it will be useful, |
  14. | but WITHOUT ANY WARRANTY; without even the implied warranty of  |
  15. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   |
  16. | GNU General Public License for more details.                    |
  17. +-----------------------------------------------------------------+
  18. | Author: blackberry                   <src.blackberry@yahoo.com> |
  19. | Date:   2011-09-24 (original version); 2017-08-16 (minor fixes) |
  20. \*---------------------------------------------------------------*/
  21.  
  22. #include <cstdio>
  23. #include <cstring>
  24. #include <cctype>
  25. #include <stack>
  26. #include <map>
  27. #include <cmath>
  28.  
  29.  
  30. typedef enum _SY_TOKEN_TYPE
  31. {
  32.     SY_TT_OPERATOR, SY_TT_NUMBER
  33. } SY_TOKEN_TYPE;
  34.  
  35. typedef struct _SY_TOKEN
  36. {
  37.     SY_TOKEN_TYPE type;
  38.     union
  39.     {
  40.         char op;
  41.         double num;
  42.     };
  43.    
  44.     _SY_TOKEN(void) : type(SY_TT_NUMBER), num(0) { }
  45.     _SY_TOKEN(char _op) : type(SY_TT_OPERATOR), op(_op) { }
  46. } SY_TOKEN;
  47.  
  48. typedef enum _SY_OP_ASSOCTYPE
  49. {
  50.     SY_OP_LEFTASSOC, SY_OP_RIGHTASSOC
  51. } SY_OP_ASSOCTYPE;
  52.  
  53. typedef std::stack<SY_TOKEN *> SY_TOKEN_STACK;
  54.  
  55. SY_TOKEN_STACK *sy_tokenize(char *str)
  56. {
  57.     char prevchar = 0;
  58.     SY_TOKEN *tok;
  59.     SY_TOKEN_STACK inp;
  60.     SY_TOKEN_STACK *stream = new SY_TOKEN_STACK;
  61.     double _exp, multiplier;
  62.     bool negnum;
  63.    
  64.     for(; *str; str++)
  65.     {
  66.         if (strchr(" \t\r\n", *str)) continue;
  67.         switch(*str)
  68.         {
  69.             case '+': case '-': case '*': case '/':
  70.             case '(': case ')': case '^':
  71.                 if (*str != '-' || (*str == '-' && !strchr("(*/^", prevchar) && prevchar))
  72.                 {
  73.                     if (*str == '(' && strchr("0123456789)", prevchar) && prevchar)
  74.                         inp.push(new SY_TOKEN('*'));
  75.                     inp.push(new SY_TOKEN(*str));
  76.                     if (*str == ')' && isdigit(str[1]))
  77.                         inp.push(new SY_TOKEN('*'));
  78.                     break;
  79.                 }
  80.             case '0': case '1': case '2': case '3': case '4':
  81.             case '5': case '6': case '7': case '8': case '9':
  82.                 tok = new SY_TOKEN;
  83.                 _exp = 1;
  84.                 negnum = (*str == '-');
  85.                 if (*str == '-') str++;
  86.                 multiplier = 10;
  87.                 for(; *str; str++)
  88.                 {
  89.                     if (*str == '.')
  90.                     {
  91.                         if (multiplier == 0.1 || !isdigit(str[1]))
  92.                             return 0;
  93.                         _exp = 0.1;
  94.                         multiplier = 0.1;
  95.                         continue;
  96.                     }
  97.                     if (*str < '0' || *str > '9')
  98.                     {
  99.                         break;
  100.                     }
  101.                     tok->num = tok->num * _exp + (*str - '0');
  102.                     _exp *= multiplier;
  103.                 }
  104.                 str--;
  105.                 if (negnum) tok->num *= -1;
  106.                 inp.push(tok);
  107.                 break;
  108.             default:
  109.                 return 0;
  110.         }
  111.         prevchar = *str;
  112.     }
  113.    
  114.     while(inp.size())
  115.     {
  116.         stream->push(inp.top());
  117.         inp.pop();
  118.     }
  119.     return stream;
  120. }
  121.  
  122. unsigned int sy_op_precedence(SY_TOKEN *op)
  123. {
  124.     std::map<char, unsigned int> prectab;
  125.     prectab['^'] = 3;
  126.     prectab['*'] = 2;
  127.     prectab['/'] = 2;
  128.     prectab['+'] = 1;
  129.     prectab['-'] = 1;
  130.     return prectab[op->op];
  131. }
  132.  
  133. SY_OP_ASSOCTYPE sy_op_assoctype(SY_TOKEN *op)
  134. {
  135.     return op->op == '^' ? SY_OP_RIGHTASSOC : SY_OP_LEFTASSOC;
  136. }
  137.  
  138. SY_TOKEN_STACK *sy_rearrange(SY_TOKEN_STACK *inp)
  139. {
  140.     SY_TOKEN_STACK *out_stack = new SY_TOKEN_STACK;
  141.     SY_TOKEN_STACK op_stack;
  142.    
  143.     while(!inp->empty())
  144.     {
  145.         SY_TOKEN *tok = inp->top();
  146.         inp->pop();
  147.        
  148.         if (tok->type == SY_TT_NUMBER)
  149.         {
  150.             out_stack->push(tok);
  151.         }
  152.         else switch(tok->op)
  153.         {
  154.             case '+': case '-': case '*': case '/': case '^':
  155.                 while(op_stack.size())
  156.                 {
  157.                     if ((sy_op_assoctype(op_stack.top()) == SY_OP_RIGHTASSOC && sy_op_precedence(tok) < sy_op_precedence(op_stack.top())) || (sy_op_assoctype(op_stack.top()) == SY_OP_LEFTASSOC && sy_op_precedence(tok) <= sy_op_precedence(op_stack.top())))
  158.                     {
  159.                         out_stack->push(op_stack.top());
  160.                         op_stack.pop();
  161.                     }
  162.                     else break;
  163.                 }
  164.             case '(':
  165.                 op_stack.push(tok);
  166.                 break;
  167.             case ')':
  168.                 while(op_stack.size() && op_stack.top()->op != '(')
  169.                 {
  170.                     out_stack->push(op_stack.top());
  171.                     op_stack.pop();
  172.                 }
  173.                
  174.                 if (!op_stack.size())
  175.                     return 0;
  176.                 delete tok;
  177.                 delete op_stack.top();
  178.                 op_stack.pop();
  179.                 break;
  180.         }
  181.     }
  182.     while(op_stack.size())
  183.     {
  184.         out_stack->push(op_stack.top());
  185.         op_stack.pop();
  186.     }
  187.     return out_stack;
  188. }
  189.  
  190. double sy_eval_token_stack(SY_TOKEN_STACK *out_stack)
  191. {
  192.     if (!out_stack->size())
  193.         return NAN;
  194.     SY_TOKEN tok = *(out_stack->top());
  195.     delete out_stack->top();
  196.     out_stack->pop();
  197.    
  198.     if (tok.type == SY_TT_NUMBER)
  199.     {
  200.         return tok.num;
  201.     }
  202.     else
  203.     {
  204.         double op2 = sy_eval_token_stack(out_stack);
  205.         double op1 = sy_eval_token_stack(out_stack);
  206.        
  207.         if (op1 == NAN || op2 == NAN)
  208.             return NAN;
  209.        
  210.         switch(tok.op)
  211.         {
  212.             case '+':
  213.                 return op1 + op2;
  214.             case '-':
  215.                 return op1 - op2;
  216.             case '*':
  217.                 return op1 * op2;
  218.             case '/':
  219.                 if (op2 == 0)
  220.                     return NAN;
  221.                 return op1 / op2;
  222.             case '^':
  223.                 return pow(op1, op2);
  224.             default:
  225.                 return NAN;
  226.         }
  227.     }
  228. }
  229.  
  230. double sy_eval_expr(char *str)
  231. {
  232.     SY_TOKEN_STACK *ts;
  233.     double result;
  234.    
  235.     if (!(ts = sy_tokenize(str)))
  236.         return NAN;
  237.     if (!(ts = sy_rearrange(ts)))
  238.         return NAN;
  239.     result = sy_eval_token_stack(ts);
  240.    
  241.     if (ts->size())
  242.     {
  243.         while(ts->size())
  244.         {
  245.             delete ts->top();
  246.             ts->pop();
  247.         }
  248.         return NAN;
  249.     }
  250.    
  251.     return result;
  252. }
  253.  
  254. int main(void)
  255. {
  256.     while(!feof(stdin))
  257.     {
  258.         char buffer[1024];
  259.         printf("$ ");
  260.         fgets(buffer, sizeof(buffer), stdin);
  261.         if (!strcmp(buffer, "\n")) break;
  262.         printf("    = %g\n", sy_eval_expr(buffer));
  263.     }
  264.     return 0;
  265. }
  266.  
  267. /* ----------------------------------------------------------- */
  268. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement