Click here to Skip to main content
15,884,298 members
Articles / Programming Languages / C++

Expression evaluator : using RPN

Rate me:
Please Sign up or sign in to vote.
4.77/5 (28 votes)
5 Nov 20033 min read 213.9K   3.3K   63  
An article showing how to evaluate mathematical expressions using reverse polish notation (RPN)
#include <stdlib.h>
#include <string.h>
#include "expressionevaluator.h"

/*
  History
  ----------
  Sunday, November 2, 2003
             * Initial version

  Monday, November 3, 2003
             * Fixed precedence rule of multiplication

*/

/* ----------------------------------------------------------------------------- 
 * Copyright (c) 2003 Lallous <lallousx86@yahoo.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ----------------------------------------------------------------------------- 
 */

/*

  ExpressionEvaluator is a set of functions allowing you to evaluate a given expression.
  Currently two versions are exported from the ExpressionEvaluator namespace:

  int ExpressionEvaluator::calculateLong(std::string expr, long &result);
  int ExpressionEvaluator::calculateDouble(std::string expr, double &result);

  Both functions might return an error code defined in the header file / enum having
  set of values as eval_XXXX.
  To check for success, do this:
  if (ExpressionEvaluator::calculateXXX(expr, xxx) == ExpressionEvaluator::eval_ok)
  {
    // .......
  }

*/


namespace ExpressionEvaluator
{
  using std::string;
  const char *operator_mul   = "*";
  const char *operator_div   = "/";
  const char *operator_idiv  = "\\";
  const char *operator_mod   = "%";
  const char *operator_add   = "+";
  const char *operator_sub   = "-";
  const char *operator_xor   = "^";
  const char *operator_band  = "&";
  const char *operator_bor   = "|";
  const char *operator_land  = "&&";
  const char *operator_lor   = "||";
  const char *operator_nor   = "!|";
  const char *operator_nand  = "!&";
  const char *operator_iseq  = "==";
  const char *operator_shl   = "<<";
  const char *operator_shr   = ">>";
  const char *operator_lt    = "<";
  const char *operator_gt    = ">";

  struct operator_t
  {
    const char *op;
    int  precedence;
  };

  const operator_t operators[] =
  {
    {"<>=*/%+-^&|\\", -1}, // Operators[0] is reserved for storing all symbols that can be used in operators
    {operator_mul, 19}, 
    {operator_div, 19}, {operator_idiv, 19}, 
    {operator_mod, 18},
    {operator_shl, 17}, {operator_shr, 17},
    {operator_sub, 16}, {operator_add, 16},
    {operator_xor, 15}, {operator_band, 15}, {operator_bor, 15}, {operator_nor, 15}, 
    {operator_land, 14}, {operator_lor, 14}, {operator_nand, 15},
    {operator_iseq, 13}, {operator_lt, 13}, {operator_gt, 13}
  };
  
  // Returns < 0 if 'op' is not an operator
  // Otherwise it returns the index of the operator in the 'operators' array
  // The index can be used to get the operator's precedence value.
  int isOperator(string op)
  {
    int i = 0, oplen = 0;
    string s = operators[0].op;
    // scan as long as it is a valid operator
    // an operator might  have not just one symbol to represent it

    while (i<op.length())
    {
      if (s.find(op[i]) == s.npos)
        break;
      oplen++;
      i++;
    }

    // no valid symbol in operator!s
    if (!oplen)
      return -1;

    // identify what operator that is and return its index
    // 1 = highest , N = lowest
    // -1 = invalid operator
    s = op.substr(0, oplen);
    for (i=1; i<sizeof(operators)/sizeof(char *); i++)
    {
      if (s == operators[i].op)
        return i;
    }
    return -1;
  }

  // returns the operands length.
  // scans as long as the current value is an alphanumeric or a decimal seperator
  int getToken(string str)
  {
    int i=0, tokenLen = 0;
    while ( (i<str.length()) && (isalnum(str[i]) || (str[i]=='.')))
    {
      tokenLen++;
      i++;
    }
    return tokenLen;
  }

  int toRPN(string exp, string &rpn)
  {
    std::stack<string> st;
    string token, topToken;
    char token1;
    int  tokenLen, topPrecedence, idx, precedence;
    string SEP(" "), EMPTY("");

    rpn = "";

    for (int i=0; i<exp.length();i++)
    {
      token1 = exp[i];

      // skip white space
      if (isspace(token1))
        continue;

      // push left parenthesis
      else if (token1 == '(')
      {
        st.push(EMPTY+token1);
        continue;
      }

      // flush all stack till matching the left-parenthesis
      else if (token1 == ')')
      {
        for (;;)
        {
          // could not match left-parenthesis
          if (st.empty())
            return eval_unbalanced;

          topToken = st.top();
          st.pop();
          if (topToken == "(")
            break;
          rpn.append(SEP + topToken);
        }
        continue;
      }

      // is this an operator?
      idx = isOperator(exp.substr(i));
    
      // not an operator
      if (idx < 0)
      {
        tokenLen = getToken(exp.substr(i));
        token = exp.substr(i, tokenLen);
        rpn.append(SEP + token);
        i += tokenLen - 1;
        continue;
      }

      // is an operator
      else
      {
        // get precedence
        precedence = operators[idx].precedence;
        topPrecedence = 0;

        // get current operator 
        tokenLen = strlen(operators[idx].op);
        token    = exp.substr(i, tokenLen);
        i += tokenLen - 1;
        for (;;)
        {
          // get top's precedence
          if (!st.empty())
          {
            topToken = st.top();
            idx = isOperator(topToken.c_str());
            if (idx < 0)
              topPrecedence = 1; // give a low priority if operator not ok!
            else
              topPrecedence = operators[idx].precedence;
          }

          if (st.empty() || st.top() == "(" ||
              precedence > topPrecedence
             )
          {
            st.push(token);
            break;
          }
          // operator has lower precedence then pop it
          else
          {
            st.pop();
            rpn.append(SEP + topToken);
          }
        }
        continue;
      }
    }

    for (;;)
    {
      if (st.empty())
        break;
      topToken = st.top();
      st.pop();
      if (topToken != "(")
        rpn.append(SEP + topToken);
      else
      {
        return eval_unbalanced;
      }
    }
    return eval_ok;
  }

  template <typename T>
  int evaluateRPN(string rpn, T &result)
  {
    std::stack<T> st;
    char token1;
    string token;
    T r, op1, op2;
    int idx, tokenLen;

    for (int i=0;i<rpn.length();i++)
    {
      token1 = rpn[i];
      if (isspace(token1))
        continue;

      idx = isOperator(rpn.substr(i));

      // not an operator
      if (idx < 0)
      {
        tokenLen = getToken(rpn.substr(i));
        token    = rpn.substr(i, tokenLen);
        double d = atof(token.c_str());
        r = d;
        st.push(r);
        i += tokenLen - 1;
        continue;
      }
      // an operator
      else
      {
        // get operand1 and op2
        op2 = st.top(); st.pop();
        op1 = st.top(); st.pop();

        tokenLen = strlen(operators[idx].op);
        token    = rpn.substr(i, tokenLen);
        i += tokenLen - 1;

        if (token == operator_mul)
          r = op1 * op2;
        else if (token == operator_idiv)
          r = (long) (op1 / op2);
        else if (token == operator_div)
          r = op1 / op2;
        else if (token == operator_mod)
          r = (long)op1 % (long)op2;
        else if (token == operator_add)
          r = op1 + op2;
        else if (token == operator_sub)
          r = op1 - op2;
        else if (token == operator_land)
          r = op1 && op2;
        else if (token == operator_band)
          r = (long)op1 & (long)op2;
        else if (token == operator_lor)
          r = op1 || op2;
        else if (token == operator_bor)
          r = (long)op1 | (long)op2;
        else if (token == operator_xor)
          r = (long)op1 ^ (long)op2;
        else if (token == operator_nor)
          r = ~((long)op1 | (long)op2);
        else if (token == operator_nand)
          r = ~((long)op1 & (long)op2);
        else if (token == operator_iseq)
          r = op1 == op2;
        else if (token == operator_shl)
          r = (long)op1 << (long)op2;
        else if (token == operator_shr)
          r = (long)op1 >> (long)op2;
        else if (token == operator_lt)
          r = op1 < op2;
        else if (token == operator_gt)
          r = op1 > op2;
        // push result
        st.push(r);
      }
    }
    result = st.top();
    st.pop();
    if (!st.empty())
      return eval_evalerr;
    return eval_ok;
  }

  template <typename T> int calculate(string expr, T &r)
  {
    string rpn;
    int err;
    if ( (err = toRPN(expr, rpn)) != eval_ok)
      return err;
    err = evaluateRPN(rpn, r);
    return err;
  }
  
  int calculateLong(string expr, long &r)
  {
    return calculate(expr, r);
  }

  int calculateDouble(string expr, double &r)
  {
    return calculate(expr, r);
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Elias (aka lallousx86, @0xeb) has always been interested in the making of things and their inner workings.

His computer interests include system programming, reverse engineering, writing libraries, tutorials and articles.

In his free time, and apart from researching, his favorite reading topics include: dreams, metaphysics, philosophy, psychology and any other human/mystical science.

Former employee of Microsoft and Hex-Rays (the creators of IDA Pro), was responsible about many debugger plugins, IDAPython project ownership and what not.

Elias currently works as an Anticheat engineer in Blizzard Entertainment.

Elias co-authored 2 books and authored one book:

- Practical Reverse Engineering
- The Antivirus Hacker's Handbook
- The Art of Batch Files Programming

Comments and Discussions