Advertisement
pablo7890

math007

Apr 26th, 2013
341
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.51 KB | None | 0 0
  1. // (c) Peter Kankowski, 2007. http://smallcode.weblogs.us mailto:[email protected]
  2. #include <assert.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6.  
  7. // ================================
  8. //   Simple expression evaluator
  9. // ================================
  10.  
  11. // Error codes
  12. enum EXPR_EVAL_ERR {
  13.     EEE_NO_ERROR = 0,
  14.     EEE_PARENTHESIS = 1,
  15.     EEE_WRONG_CHAR = 2,
  16.     EEE_DIVIDE_BY_ZERO = 3
  17. };
  18.  
  19. typedef char EVAL_CHAR;
  20.  
  21. class ExprEval {
  22. private:
  23.     EXPR_EVAL_ERR _err;
  24.     EVAL_CHAR* _err_pos;
  25.     int _paren_count;
  26.  
  27.     // Parse a number or an expression in parenthesis
  28.     double ParseAtom(EVAL_CHAR*& expr) {
  29.         // Skip spaces
  30.         while(*expr == ' ')
  31.             expr++;
  32.  
  33.         // Handle the sign before parenthesis (or before number)
  34.         bool negative = false;
  35.         if(*expr == '-') {
  36.             negative = true;
  37.             expr++;
  38.         }
  39.         if(*expr == '+') {
  40.             expr++;
  41.         }
  42.  
  43.         // Check if there is parenthesis
  44.         if(*expr == '(') {
  45.             expr++;
  46.             _paren_count++;
  47.             double res = ParseSummands(expr);
  48.             if(*expr != ')') {
  49.                 // Unmatched opening parenthesis
  50.                 _err = EEE_PARENTHESIS;
  51.                 _err_pos = expr;
  52.                 return 0;
  53.             }
  54.             expr++;
  55.             _paren_count--;
  56.             return negative ? -res : res;
  57.         }
  58.  
  59.         // It should be a number; convert it to double
  60.         char* end_ptr;
  61.         double res = strtod(expr, &end_ptr);
  62.         if(end_ptr == expr) {
  63.             // Report error
  64.             _err = EEE_WRONG_CHAR;
  65.             _err_pos = expr;
  66.             return 0;
  67.         }
  68.         // Advance the pointer and return the result
  69.         expr = end_ptr;
  70.         return negative ? -res : res;
  71.     }
  72.  
  73.     // Parse multiplication and division
  74.     double ParseFactors(EVAL_CHAR*& expr) {
  75.         double num1 = ParseAtom(expr);
  76.         for(;;) {
  77.             // Skip spaces
  78.             while(*expr == ' ')
  79.                 expr++;
  80.             // Save the operation and position
  81.             EVAL_CHAR op = *expr;
  82.             EVAL_CHAR* pos = expr;
  83.             if(op != '/' && op != '*')
  84.                 return num1;
  85.             expr++;
  86.             double num2 = ParseAtom(expr);
  87.             // Perform the saved operation
  88.             if(op == '/') {
  89.                 // Handle division by zero
  90.                 if(num2 == 0) {
  91.                     _err = EEE_DIVIDE_BY_ZERO;
  92.                     _err_pos = pos;
  93.                     return 0;
  94.                 }
  95.                 num1 /= num2;
  96.             }
  97.             else
  98.                 num1 *= num2;
  99.         }
  100.     }
  101.  
  102.     // Parse addition and subtraction
  103.     double ParseSummands(EVAL_CHAR*& expr) {
  104.         double num1 = ParseFactors(expr);
  105.         for(;;) {
  106.             // Skip spaces
  107.             while(*expr == ' ')
  108.                 expr++;
  109.             EVAL_CHAR op = *expr;
  110.             if(op != '-' && op != '+')
  111.                 return num1;
  112.             expr++;
  113.             double num2 = ParseFactors(expr);
  114.             if(op == '-')
  115.                 num1 -= num2;
  116.             else
  117.                 num1 += num2;
  118.         }
  119.     }
  120.  
  121. public:
  122.     double Eval(EVAL_CHAR* expr) {
  123.         _paren_count = 0;
  124.         _err = EEE_NO_ERROR;
  125.         double res = ParseSummands(expr);
  126.         // Now, expr should point to '\0', and _paren_count should be zero
  127.         if(_paren_count != 0 || *expr == ')') {
  128.             _err = EEE_PARENTHESIS;
  129.             _err_pos = expr;
  130.             return 0;
  131.         }
  132.         if(*expr != '\0') {
  133.             _err = EEE_WRONG_CHAR;
  134.             _err_pos = expr;
  135.             return 0;
  136.         }
  137.         return res;
  138.     };
  139.     EXPR_EVAL_ERR GetErr() {
  140.         return _err;
  141.     }
  142.     EVAL_CHAR* GetErrPos() {
  143.         return _err_pos;
  144.     }
  145. };
  146.  
  147. // =======
  148. //  Tests
  149. // =======
  150.  
  151. #ifdef _DEBUG
  152. void TestExprEval() {
  153.     ExprEval eval;
  154.     // Some simple expressions
  155.     assert(eval.Eval("1234") == 1234 && eval.GetErr() == EEE_NO_ERROR);
  156.     assert(eval.Eval("1+2*3") == 7 && eval.GetErr() == EEE_NO_ERROR);
  157.  
  158.     // Parenthesis
  159.     assert(eval.Eval("5*(4+4+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
  160.     assert(eval.Eval("5*(2*(1+3)+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
  161.     assert(eval.Eval("5*((1+3)*2+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
  162.  
  163.     // Spaces
  164.     assert(eval.Eval("5 * ((1 + 3) * 2 + 1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
  165.     assert(eval.Eval("5 - 2 * ( 3 )") == -1 && eval.GetErr() == EEE_NO_ERROR);
  166.     assert(eval.Eval("5 - 2 * ( ( 4 )  - 1 )") == -1 && eval.GetErr() == EEE_NO_ERROR);
  167.  
  168.     // Sign before parenthesis
  169.     assert(eval.Eval("-(2+1)*4") == -12 && eval.GetErr() == EEE_NO_ERROR);
  170.     assert(eval.Eval("-4*(2+1)") == -12 && eval.GetErr() == EEE_NO_ERROR);
  171.    
  172.     // Fractional numbers
  173.     assert(eval.Eval("1.5/5") == 0.3 && eval.GetErr() == EEE_NO_ERROR);
  174.     assert(eval.Eval("1/5e10") == 2e-11 && eval.GetErr() == EEE_NO_ERROR);
  175.     assert(eval.Eval("(4-3)/(4*4)") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
  176.     assert(eval.Eval("1/2/2") == 0.25 && eval.GetErr() == EEE_NO_ERROR);
  177.     assert(eval.Eval("0.25 * .5 * 0.5") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
  178.     assert(eval.Eval(".25 / 2 * .5") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
  179.    
  180.     // Repeated operators
  181.     assert(eval.Eval("1+-2") == -1 && eval.GetErr() == EEE_NO_ERROR);
  182.     assert(eval.Eval("--2") == 2 && eval.GetErr() == EEE_NO_ERROR);
  183.     assert(eval.Eval("2---2") == 0 && eval.GetErr() == EEE_NO_ERROR);
  184.     assert(eval.Eval("2-+-2") == 4 && eval.GetErr() == EEE_NO_ERROR);
  185.  
  186.     // === Errors ===
  187.     // Parenthesis error
  188.     eval.Eval("5*((1+3)*2+1");
  189.     assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), "") == 0);
  190.     eval.Eval("5*((1+3)*2)+1)");
  191.     assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), ")") == 0);
  192.    
  193.     // Repeated operators (wrong)
  194.     eval.Eval("5*/2");
  195.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "/2") == 0);
  196.    
  197.     // Wrong position of an operator
  198.     eval.Eval("*2");
  199.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "*2") == 0);
  200.     eval.Eval("2+");
  201.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
  202.     eval.Eval("2*");
  203.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
  204.    
  205.     // Division by zero
  206.     eval.Eval("2/0");
  207.     assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0") == 0);
  208.     eval.Eval("3+1/(5-5)+4");
  209.     assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/(5-5)+4") == 0);
  210.     eval.Eval("2/"); // Erroneously detected as division by zero, but that's ok for us
  211.     assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/") == 0);
  212.    
  213.     // Invalid characters
  214.     eval.Eval("~5");
  215.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "~5") == 0);
  216.     eval.Eval("5x");
  217.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "x") == 0);
  218.  
  219.     // Multiply errors
  220.     eval.Eval("3+1/0+4$"); // Only one error will be detected (in this case, the last one)
  221.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "$") == 0);
  222.     eval.Eval("3+1/0+4");
  223.     assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0+4") == 0);
  224.     eval.Eval("q+1/0)"); // ...or the first one
  225.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "q+1/0)") == 0);
  226.     eval.Eval("+1/0)");
  227.     assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), ")") == 0);
  228.     eval.Eval("+1/0");
  229.     assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0") == 0);
  230.    
  231.     // An emtpy string
  232.     eval.Eval("");
  233.     assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
  234. }
  235. #endif
  236.  
  237. // ============
  238. // Main program
  239. // ============
  240.  
  241. int main() {
  242. #ifdef _DEBUG
  243.     TestExprEval();
  244. #endif
  245.     static const char *errors[] = {
  246.         "no error",
  247.         "parentheses don't match",
  248.         "invalid character",
  249.         "division by zero"};
  250.  
  251.     puts("Enter an expression (or an empty string to exit):");
  252.     for(;;) {
  253.         // Get a string from console
  254.         static char buff[256];
  255.         char *expr = gets_s(buff, sizeof(buff));
  256.  
  257.         // If the string is empty, then exit
  258.         if(*expr == '\0')
  259.             return 0;
  260.  
  261.         // Evaluate the expression
  262.         ExprEval eval;
  263.         double res = eval.Eval(expr);
  264.         if(eval.GetErr() != EEE_NO_ERROR) {
  265.             printf("  Error: %s at %s\n", errors[eval.GetErr()], eval.GetErrPos());
  266.         } else {
  267.             printf("  = %g\n", res);
  268.         }
  269.     }
  270. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement