Search code examples
c++linked-liststackcalculatorrpn

RPN calculator c++ error handling and multiple operators


Edit:: I've made changes to my program. Take a look at calc.cpp. In particular, the isOperator function, and the fourth while loop-- that is

while (isOperator(std::cin.peek()))
...
   ...
   if ( op == '/')

I believe the problem is that char op isn't being set to '/'. When I go back into my isOperator function

int isOperator(char ch)
{
   int count = 0;
   char ops[] = {'-','+','*','/', '^'};
   for (int i = 0; i < 4; i++)
   {   
       if (ch == ops[i])
          count++;
   }
   return count;
}

and change

char ops[] = {'^','-','+','*','/'};

to

char ops[] = {'-','+','*','/', '^'};

op can be set to '/', but not '^'. There must be something simple I'm not seeing.

Dstack.h (stack class):

#ifndef DSTACK_H
#define DSTACK_H

#include <iostream>

class Dstack
{
   public:
      Dstack();
      ~Dstack();
      void push(double value);
      bool pop(double &value);
      double top();
      int size();
      bool empty();
      bool print();
  private:
      class Node
      {
         public:
            Node(double value, Node* next)
            {m_value = value; m_next = next;}
            double m_value;
            Node* m_next;
     };
     Node* m_head;
 };


#endif 

Dstack.cpp (stack function definitions):

#include "dstack.h"
#include <iostream>

Dstack::Dstack()
{
   m_head = NULL;
}

void Dstack::push(double value)
{
   m_head = new Node (value, m_head);
}

bool Dstack::pop(double &value)
{
   if (m_head == NULL)
   return false;

   value = m_head->m_value;
   Node *temp = m_head;
   m_head = m_head->m_next;
   delete temp;
   return true;
}

double Dstack::top()
{
   double value = m_head->m_value;
   return value;
}         

int Dstack::size()
{
  int count = 0;
  Node *ptr = m_head;
  while (ptr != NULL)
  {
     count++; 
     ptr = ptr->m_next;
  }
  return count;
}   

bool Dstack::empty()
{
   if (m_head == NULL)
   return true;

   return false;
}

Dstack::~Dstack()
{
   Node* ptr = m_head;
   while (ptr != NULL)
   {
      Node* temp = ptr;
      ptr = ptr->m_next;
      delete temp;
   } 
}

bool Dstack::print()
{
   //if (m_head->m_next == NULL)
   //return false;

   std::cout << m_head->m_value << std::endl;
   return true;
}

Calc.cpp (calculator functions)

#include "dstack.h"
#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <sstream> 

int isOperator(char ch) 
{ 
   int count = 0;  
   char ops[] = {'-','+','*','^','/'};
   for (int i = 0; i < 4; i++) 
   {   
      if (ch == ops[i])
      count++;
   }   
   return count;
}


int main()
{ 
   Dstack stack;
   double num;
   double result = 0;
   char op = '\0';
   double a = 0;
   double b = 0;


   while (std::cin.peek() != EOF)
   {   
      std::cin >> std::ws;
      while (isdigit(std::cin.peek()))
      {     
         std::cin >> num;
         stack.push(num);

         while(isspace (std::cin.peek()))
         {   
            std::cin.ignore();
            std::cin.peek();
         }   
     }   

     while (isOperator(std::cin.peek())) 
     {

        //make sure there is more than one operand to calculate
        if (stack.size() <2)
        {
           std::cerr << "Error: Invalid Expression." << std::endl;
           exit(1);
        }

        //make sure there are enough operators
        if (isOperator(std::cin.peek() +1 < stack.size() ))
        {
           std::cerr << "Error: Invalid Expression." << std::endl;
           exit(1);
        }

        //clear white space for each cycle
        std::cin >> std::ws; 

        //operate!
        std::cin >> op;
        if (op == '+')
        {

           b = stack.top();
           stack.pop(b);
           a = stack.top();
           stack.pop(a);
           result = a + b;
           stack.push(result);
       }
       if (op == '-')
       {
           b = stack.top();
           stack.pop(b);
           a = stack.top();
           stack.pop(a);
           result = a - b;
           stack.push(result);
       }
       if (op == '*')
       {
           b = stack.top();
           stack.pop(b);
           a = stack.top();
           stack.pop(a);
           result = a * b;
           stack.push(result);
       }
        if (op == '^')
       {
          b = stack.top();
          stack.pop(b);
          a = stack.top();
          stack.pop(a);

          result = pow(a,b);
          stack.push(result);
      }
       if (op == '/')
        {
           b = stack.top();
           stack.pop(b);
           a = stack.top();
           stack.pop(a);

           //b cant equal 0!!!
           if (b == 0)
           {
              std::cerr << "Error: Invalid expression." << std::endl;
              exit(1);
           }
           result = a / b;
           stack.push(result);
        }

        std::cout << op << std::endl;

        //move char to next position    
        std::cin.peek();

        //ignore possible white spaces left in expression and repeat
        while (isspace (std::cin.peek()))
        {
           std::cin.ignore();
           std::cin.peek();
        }
     }
}
   if (stack.size() == 1)
      std::cout << result << std::endl;
   else
   {
      std::cerr << "Error: Invalid expression." << std::endl;
      exit(1);
   }

   return 0;
}

Solution

  • For your problem, I would recommend a total refactoring of your input handling.

    First of all to read whole lines, and then to parse the input line character by character. That way it's actually easier to recognize and handle operators of all kinds.

    For example: Get the next character from the input line, and check what type of character it is:

    • For white-space, just discard the character and continue with the next
    • If it's a digit, fetch characters while it's a digit and construct the number from that
    • If it's a valid operator then handle it
    • If it's something else then handle the error (for example by skipping the current line and read the next)

    As you can see a kind of error handling of input is kind of built-in into the method described above.

    It's also easy to expand the above to recognize symbols, that can be used for variables or functions.


    The main loops could look something like this in code:

    while (std::getline(std::cin, line))
    {
        for (size_t current_char_index = 0; current_char_index < line-size(); ++current_char_index)
        {
            // TODO: Handle current character at line[current_char_index]
        }
    }
    

    For practicality I also suggest splitting main code into functions.

    Lastly, and unless your exercise is about making your own stack class, use std::stack.