/**********************************************************************
 * Tom Kelliher, CS 220
 *
 * postfix.c -- Evaluate integer postfix expressions, one per line.
 *    Type q at the beginning of a line to exit.
 **********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>


#define STACK_SIZE 128
#define LINE_LENGTH 80

// Declarations for the stack.  Stack grows down in memory.

int stack[STACK_SIZE];
int *stackPointer = stack + STACK_SIZE;

// A line of input is read into the line array and characters removed
// from it.
char line[LINE_LENGTH];

// Points to the next input character in line to be processed.
char *next;


// Prototypes.
int isEmpty(void);
int isFull(void);
void push(int item);
int pop(void);
void die(const char *s);
int getValue(void);
void compute(void);
void output(void);
void printVal(int v);


/**********************************************************************
 * main()
 **********************************************************************/

int main()
{
  printf("Hit me with some postfix expressions.\n");
  printf("Type q to quit.\n\n? ");
  fgets(line, LINE_LENGTH, stdin);   // Read an expression.
  next = line;

  while (*line != 'q')   // Keep reading postfix expressions until user
                         // wants to quit.
    {
      // Decide what to do with the current character.

      // Read a number and push onto stack.
      if (isdigit(*next))
        push(getValue());

      // Determine if '-' is operator or part of operand and act
      // accordingly.
      else if (*next == '-')
        if (isdigit(*(next+1)))   // Read a negative value.
          {
            ++next;
            push(-getValue());
          }
        else                 // Subtraction operator.
          compute();

      // Operate on two top of stack operands, push result.
      else if (*next == '+' || *next == '*' || *next == '/')
        compute();

      // Output result of computation.
      else if (*next == '\0')
        {
          output();
          printf("? ");   // Read next expression.
          fgets(line, LINE_LENGTH, stdin);
          next = line;
        }
        
      // Skip over.
      else if (isspace(*next))
        ++next;

      // Error.
      else
        die("Unrecognized input character.");
    }
        

  return 0;
}


/**********************************************************************
 * isEmpty() --- returns 1 if the stack is empty.
 **********************************************************************/

int isEmpty(void)
{
  return stackPointer == stack + STACK_SIZE;
}


/**********************************************************************
 * isFull() --- returns 1 if the stack is full.
 **********************************************************************/

int isFull(void)
{
  return stackPointer == stack;
}


/**********************************************************************
 * push() --- push item onto the stack.
 **********************************************************************/

void push(int item)
{
  if (isFull())
    die("Trying to push onto a full stack");

  *--stackPointer = item;
}


/**********************************************************************
 * pop() --- pop and return the top of stack item.
 **********************************************************************/

int pop(void)
{
  if (isEmpty())
    die("Trying to pop from an empty stack.");

  return *stackPointer++;
}


/**********************************************************************
 * die() --- print s and die.
 **********************************************************************/

void die(const char *s)
{
  printf("%s\n", s);
  exit(1);
}


/**********************************************************************
 * getValue() --- return an integer input.  next points to first digit.
 **********************************************************************/

int getValue(void)
{
  int value = 0;

  while(isdigit(*next))
    {
      value = 10 * value + *next - '0';
      ++next;
    }

  return value;
}


/**********************************************************************
 * compute() --- perform the operation indicated in next on the two
 *    two top of stack operands.  Push the result back onto the
 *    stack.
 **********************************************************************/

void compute(void)
{
  int i, j;

  j = pop();
  i = pop();

  switch (*next)
    {
    case '+':
      i += j;
      break;

    case '-':
      i -= j;
      break;

    case '*':
      i *= j;
      break;

    case '/':
      if (j == 0)
        die("Division by 0.");

      i /= j;
      break;
    }

  push(i);
  ++next;
}


/**********************************************************************
 * output() --- print the result of the computation.  The stack
 *    should be empty after the pop().
 **********************************************************************/

void output(void)
{
  printVal(pop());
  printf("\n");

  if (!isEmpty())
    die("Stack should be empty after printing a result.");
}


/**********************************************************************
 * printVal() --- using recursion, print the value of the argument.
 **********************************************************************/

void printVal(int v)
{
   char digit;

   if (v < 0)         /* If negative, handle the sign */
   {
      printf("-");
      printVal(-v);
   }
   else
   {
      /* Use remainder operator (%) to grab the least significant
       * digit (LSD).  Convert the digit to its ASCII equivalent by
       * adding '0'.
       */

      digit = v % 10 + '0';

      /* If there are any digits to the left of the LSD, print them. */
      
      if (v / 10 > 0)
         printVal(v / 10);

      printf("%c", digit);   /* Print the LSD. */
   }
}
