double calculator::calculate(TokenQueue_t rpn,
    std::map<std::string, double>* vars) {

  // Evaluate the expression in RPN form.
  std::stack<double> evaluation;
  while (!rpn.empty()) {
    TokenBase* base = rpn.front();
    rpn.pop();

    // Operator:
    if (base->type == OP) {
      Token<std::string>* strTok = static_cast<Token<std::string>*>(base);
      std::string str = strTok->val;
      if (evaluation.size() < 2) {
        throw std::domain_error("Invalid equation.");
      }
      double right = evaluation.top(); evaluation.pop();
      double left  = evaluation.top(); evaluation.pop();
      if (!str.compare("+")) {
        evaluation.push(left + right);
      } else if (!str.compare("*")) {
        evaluation.push(left * right);
      } else if (!str.compare("-")) {
        evaluation.push(left - right);
      } else if (!str.compare("/")) {
        evaluation.push(left / right);
      } else if (!str.compare("<<")) {
        evaluation.push((int) left << (int) right);
      } else if (!str.compare("^")) {
        evaluation.push(pow(left, right));
      } else if (!str.compare(">>")) {
        evaluation.push((int) left >> (int) right);
      } else if (!str.compare("%")) {
Example #2
0
std::string calculator::str(TokenQueue_t rpn) {
  std::stringstream ss;

  ss << "calculator { RPN: [ ";
  while (rpn.size()) {
    ss << packToken(resolve_reference(rpn.front()->clone())).str();
    rpn.pop();

    ss << (rpn.size() ? ", ":"");
  }
  ss << " ] }";
  return ss.str();
}
double calculator::calculate(const char* expr,
    std::map<std::string, double>* vars) {
  // 1. Create the operator precedence map.
  std::map<std::string, int> opPrecedence;
  opPrecedence["("] = -1;
  opPrecedence["<<"] = 1; opPrecedence[">>"] = 1;
  opPrecedence["+"]  = 2; opPrecedence["-"]  = 2;
  opPrecedence["*"]  = 3; opPrecedence["/"]  = 3;
  opPrecedence["^"] = 4;

  // 2. Convert to RPN with Dijkstra's Shunting-yard algorithm.
  TokenQueue_t rpn = toRPN(expr, vars, opPrecedence);

  // 3. Evaluate the expression in RPN form.
  std::stack<double> evaluation;
  while (!rpn.empty()) {
    TokenBase* base = rpn.front();
    rpn.pop();

    Token<std::string>* strTok = dynamic_cast<Token<std::string>*>(base);
    Token<double>* doubleTok = dynamic_cast<Token<double>*>(base);
    if (strTok) {
      std::string str = strTok->val;
      if (evaluation.size() < 2) {
        throw std::domain_error("Invalid equation.");
      }
      double right = evaluation.top(); evaluation.pop();
      double left  = evaluation.top(); evaluation.pop();
      if (!str.compare("+")) {
        evaluation.push(left + right);
      } else if (!str.compare("*")) {
        evaluation.push(left * right);
      } else if (!str.compare("-")) {
        evaluation.push(left - right);
      } else if (!str.compare("/")) {
        evaluation.push(left / right);
      } else if (!str.compare("<<")) {
        evaluation.push((int) left << (int) right);
      } else if (!str.compare("^")) {
        evaluation.push(pow(left, right));
      } else if (!str.compare(">>")) {
        evaluation.push((int) left >> (int) right);
      } else {
        throw std::domain_error("Unknown operator: '" + str + "'.");
      }
    } else if (doubleTok) {
TokenQueue_t calculator::toRPN( const char *expr, map<string, double> *vars,
                               map<string, int> opPrecedence )
{
  TokenQueue_t rpnQueue;
  stack<string> operatorStack;
  bool lastTokenWasOp = true;

  // In one pass, ignore whitespace and parse the expression into RPN
  // using Dijkstra's Shunting-yard algorithm.
  while ( *expr && isspace( *expr ) )
    ++expr;
  while ( *expr )
  {
    if ( isdigit( *expr ) )
    {
      // If the token is a number, add it to the output queue.
      char *nextChar = 0;
      double digit = strtod( expr, &nextChar );
#ifdef DEBUG
      cout << digit << endl;
#endif
      rpnQueue.push( new Token<double>(digit) );
      expr = nextChar;
      lastTokenWasOp = false;
    }
    else if ( isvariablechar( *expr ) )
    {
      // If the function is a variable, resolve it and
      // add the parsed number to the output queue.
      stringstream ss;
      ss << *expr;
      ++expr;
      while ( isvariablechar( *expr ) || isdigit( *expr ) )
      {
        ss << *expr;
        ++expr;
      }

      double *val = NULL;
      string key = ss.str();

      if ( vars )
      {
        map<string, double>::iterator it = vars->find( key );
        if ( it != vars->end() )
          val = &( it->second );
      }

      if ( val )
      {
// Save the number
#ifdef DEBUG
        cout << val << endl;
#endif
        rpnQueue.push( new Token<double>( *val ) );
        ;
      }
      else
      {
// Save the variable name:
#ifdef DEBUG
        cout << key << endl;
#endif
        rpnQueue.push( new Token<string>( key ) );
      }

      lastTokenWasOp = false;
    }
    else
    {
      // Otherwise, the variable is an operator or paranthesis.
      switch ( *expr )
      {
          case '(':
            operatorStack.push( "(" );
            ++expr;
            break;
          case ')':
            while ( operatorStack.top().compare( "(" ) )
            {
              rpnQueue.push( new Token<string>(operatorStack.top() ) );
              operatorStack.pop();
            }
            operatorStack.pop();
            ++expr;
            break;
          default:
          {
            // The token is an operator.
            //
            // Let p(o) denote the precedence of an operator o.
            //
            // If the token is an operator, o1, then
            //   While there is an operator token, o2, at the top
            //       and p(o1) <= p(o2), then
            //     pop o2 off the stack onto the output queue.
            //   Push o1 on the stack.
            stringstream ss;
            ss << *expr;
            ++expr;
            while ( *expr && !isspace( *expr ) && !isdigit( *expr ) &&
                   !isvariablechar( *expr ) && *expr != '(' && *expr != ')' )
            {
              ss << *expr;
              ++expr;
            }
            ss.clear();
            string str;
            ss >> str;
#ifdef DEBUG
            cout << str << endl;
#endif

            if ( lastTokenWasOp )
            {
              // Convert unary operators to binary in the RPN.
              if ( !str.compare("-") || !str.compare("+") )
              {
                rpnQueue.push(new Token<double>(0));
              } else {
                throw domain_error( "Unrecognized unary operator: '" + str + "'." );
              }
            }

            while ( !operatorStack.empty() &&
                opPrecedence[str] <= opPrecedence[operatorStack.top()] )
            {
              rpnQueue.push( new Token<string>(operatorStack.top() ) );
              operatorStack.pop();
            }
            operatorStack.push( str );
            lastTokenWasOp = true;
          }
      }
    }
    while ( *expr && isspace( *expr ) )
      ++expr;
  }
  while ( !operatorStack.empty() )
  {
    rpnQueue.push( new Token<string>( operatorStack.top() ) );
    operatorStack.pop();
  }
  return rpnQueue;
}