Search code examples
c++stackrpn

Recieving error "cannot dereference out of range deque iterator" when utilizing top()


I'm creating a simple program to evaluate post fix expressions but it won't run after successfully compiling due to "Expression: cannot dereference out of range deque iterator" whenever it reaches the assignment of the operand variables on lines 24 & 26. Why won't it run correctly when the program only reaches that point after filling the stack with number values?

#include <iostream>
#include <stack>
#include <string>
#include <cctype>
using namespace std;

int main() 
{
    stack<int> mystack;
    string answer; 
    int result;
    string done = "Y";
    while (done == "Y" || done == "y") {
        cout << "Please enter the RPN expression to be evaluated: ";
        getline(cin,answer);
        for(int i = 0; i < answer.length(); i++) {

            if(answer[i] == ' ' || answer[i] == ':') { 
                continue;
            } 

            if (isdigit(answer[i]) == false) {
                cout << "Token = " << answer[i];
                int operand2 = mystack.top(); 
                mystack.pop();
                int operand1 = mystack.top(); 
                mystack.pop();

                cout << " Pop " << operand2 << " Pop " << operand1;

                if(answer[i] == '+') { //operation statements
                    result = operand1 + operand2;
                }
                else if(answer[i] == '-') {
                    result = operand1 - operand2;
                }
                else if(answer[i] == '*') {
                    result =  operand1 * operand2;
                }
                else if(answer[i] == '/') {
                    result = operand1 / operand2;
                }

                mystack.push(result); //result returns to stack
                cout << " Push " << result << endl;
            }
            else if(isdigit(answer[i]) == true){
                int operand = 0; 
                while(i < answer.length() && isdigit(answer[i]) == true) {
                    operand = (operand*10) + (answer[i] - '0'); 
                    i++;
                }
                i--;
                mystack.push(operand);
                cout << "Token = " << operand << " Push " << operand << endl;
            }
        }
        cout << "Token = Pop " << mystack.top() << endl << endl;
        mystack.pop();

        cout << "type 'Y' or 'y' to continue or type any other letter to quit: ";
        getline(cin, done);
    }
}

Solution

  • Here is a simple solution to the problem. This version uses ints and uses a stack of strings. This can easily be converted to doubles by replacing int with double throughout the file,

    This uses getline() and istringstream to parse the input.

    It also uses stringstream to convert numbers to strings and back.

    You have to be careful when parsing multiple words from each line. This codes redirects the input line to ss, an istringstream object, and then takes tokens from that object.

    Also, this will print the stack if you type "s".

    Finally, you can arbitrarily load the stack with a series of space-separated numbers, or you can enter numbers one at a time. The stack is used when an operator is entered.

    #include <iostream>
    #include <stack>
    #include <string>
    #include <sstream>
    
    ///////////////////////
    /// top_and_pop()
    /// Save the top value from a stack, and then pop the stack
    /// If the stack is empty, return the string "0".
    /// If the stack was not empty, return the popped element.
    std::string top_and_pop(std::stack<std::string> &stack)
    {
      std::string return_string;
      if (stack.empty()) {
        return_string = "0";
        std::cout << "Stack Empty, returning 0" << std::endl;
    
      } else {
        return_string = stack.top();
    
        std::cout << "Popping " << return_string << std::endl;
        stack.pop();
      }
      return return_string;
    }
    
    //////////////////////
    /// is_number
    /// Parse number into a int using std::stringstream.
    /// If successful, then, the stringstream will be empty.
    /// Return: true if the str would successfully parse as a int
    bool is_number(const std::string &str)
    {
      std::istringstream ss(str);
      int number;
      ss >> number;
    
      if (!ss.fail() && ss.eof())
        return true;
    
      ss >> std::ws; // not needed in this program, because we already parsed the
      // input to a single word without whitespace.  Needed for parsing a single line
      // that may contain any set of chars.
    
      return !ss.fail() && ss.eof();
    }
    
    /////////////////////
    /// is_operator
    /// return false if str is not a single character or if an operator is not found
    ///  acceptable operators are anyof "+-*/"
    bool is_operator(const std::string &str)
    {
          return (str.length() == 1 &&
            str.find_first_not_of("+-*/") == std::string::npos);
    }
    
    /////////////////////
    /// string_to_int
    /// convert string to int
    /// Check to be sure string can convert to a int before calling
    /// if conversion fails, return 0;
    int string_to_int(const std::string &str)
    {
      std::stringstream stream;
      int number = 0;
    
      stream << str;
      stream >> number;
    
      return number;
    }
    
    //////////////////////
    /// stack_to_string
    ///
    /// print elements of stack
    std::string stack_to_string(std::stack<std::string> stack)
    {
      std::string return_string;
      while (!stack.empty()) {
        return_string += stack.top() + " ";
    
        stack.pop();
      }
      return return_string;
    }
    int main()
    {
      std::stack<std::string> mystack;
      std::string input_line;
      bool done = false;  /// flag to indicate that calcualtor is closing
      std::string token_str;
      int operand1, operand2, result;
    
      std::cout
          << "Please enter an RPN expression.  Expression may consist of any \n"
             "combination of space-separated numbers or operators.\n"
             "Operators may include '+', '-', '*', '/'.  The stack is initialized\n"
             "with an unlimited supply of zeroes.\n\n"
             "Type 's' to print the current stack.\n"
             "To exit, type a 'y'" << std::endl;
    
      do {
        getline(std::cin, input_line);
    
        std::stringstream ss(input_line);
    
        /// loop through all tokens in this input line
        while (ss >> token_str) {
          // Only numbers are allowed on the stack.
          // If the current token is a number, push it on the stack as a string
          if (is_number(token_str)) {
            mystack.push(token_str);
          } else if (is_operator(token_str)) {
            operand2 = (int) string_to_int(top_and_pop(mystack));
            operand1 = (int) string_to_int(top_and_pop(mystack));
    
            // switch does not accept string as a type, and we checked to ensure
            // it is a single character operand that is handled in the switch
            switch (token_str[0]) {
              case '+':result = operand1 + operand2;
                break;
              case '-':result = operand1 - operand2;
                break;
              case '*':result = operand1 * operand2;
                break;
              case '/':
                if (operand2 == 0) {
                  std::cout << "Error: Cannot divide by zero" << std::endl;
                  continue;  // don't push result, take new input.
                } else {
                  result = operand1 / operand2;
                }
                break;
                // default case not needed as we already checked operator, but
                // for error checking, we include it
              default:std::cout << "Operator not found" << std::endl;
                continue;
            }
            mystack.push(std::to_string(result));
            std::cout << " Push result " << result << " " << std::endl;
          } else if ("Y" == token_str || "y" == token_str) {
            done = true;
          } else if ("stack" == token_str || "s" == token_str) {
            std::cout << "stack: ";
            std::string stack_dump = stack_to_string(mystack);
            std::cout << stack_dump << std::endl;
          } else {
            std::cout << "Invalid input." << std::endl;
          }
        }
      }
      while (!done);
    }