/**********************************************************************
 * Tom Kelliher, CS 220
 *
 * postfix.c -- Evaluate integer postfix expressions, one per line.
 *    Type q at the beginning of a line to exit.
 *
 * Example postfix expression: 1 2 + 3 4 + *
 *
 * IMPORTANT HINT: Remember, pointer expressions such as
 *
 *               stack + STACK_SIZE
 *
 *               stackPointer++
 *
 *               --stackPointer
 *
 * work in units of BYTES at the assembly level.  The comments below
 * contain other IMPORTANT HINTS.
 **********************************************************************/

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


#define STACK_SIZE 8
#define LINE_LENGTH 80

// Declarations for the stack.  Stack grows up in memory.  stackPointer
// is the top-of-stack pointer.  It points to the first empty word on
// the stack.
//
// The operand stack MUST BE word-aligned.  To guarantee this, use the
// assembler directive
//
//    .align 2
//
// IMMEDIATELY BEFORE allocating space for the stack.  Otherwise, you
// will get "Unaligned address" errors when you run your program.
//
// Note that stack, stackPointer, line, and next are all global
// variables.

int stack[STACK_SIZE];   // Operand stack.
int *stackPointer;

// An entire line of input is read into the line array and individual 
// characters read 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()
{
  // Initialize globals.
  stackPointer = stack;
  next = line;

  printf("Hit me with some postfix expressions.\n");
  printf("Type q to quit.\n\n? ");
  fgets(line, LINE_LENGTH, stdin);   // Read an entire line of input
                                     // (including '\n').

  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();

      // End of input string; 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;
}


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

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


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

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

  // Write item to stack, THEN increment the pointer.
  *stackPointer++ = item;
}


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

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

  // Decrement the pointer, THEN read item from 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. */
   }
}
