#include <stdio.h>
#include <stdlib.h> /* for atof() */
#include <ctype.h>
#include <math.h>
#include <string.h>

#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
#define ALPHA '1' /* signal that a char was found */
#define POPON '2' /* signal to pop the last stack element */
#define POPOFF '3' /* signal to not pop the last stack element */
#define VARIABLE '4' /* signal that a variable was found */

int getop(char []);
void push(double);
double pop(void);
void printstack(void);
double dupstack(void);
void swapstack(void);
void clearstack(void);
void cmd(char []);
void varhandler(char []);
void varmenu(void);
void getinput(void);

/* globals for handling built-in and user-defined variables */
char uservar = '\0'; /* variable to be defined by stdin */
int nvars;
struct allvars {
 char name;
 double val;
} myvars[1000] = { /* built-in variables */
 'c', 299792458.0, /* speed of light */
 'p', 3.14, /* pi */
 '\0', 0.0 /* end of array */
};


/* reverse Polish calculator */
int main(void)
{
 int i, j, op3, op4, state, type;
 double lastval, op2;
 char s[MAXOP];
 
 varmenu(); /* print defined variables */
 getinput(); /* get line of user input */

 while ((type = getop(s)) != EOF ) {
 switch (type) {
 case NUMBER:
 printf("NUMBER returned to main\n\n");
 push(atof(s));
 break;
 case ALPHA:
 cmd(s);
 break;
 case VARIABLE:
 varhandler(s);
 break;
 case '+':
 push(pop() + pop());
 break;
 case '*':
 push(pop() * pop());
 break;
 case '-':
 op2 = pop();
 push(pop() - op2);
 break;
 case '/':
 op2 = pop();
 if (op2 != 0.0)
 push(pop() / op2);
 else
 printf("error: zero divisor\n");
 break;
 case '%':
 op3 = pop(); /* convert double to int */
 if (op3 != 0)
 push((op4 = pop()) % op3); /* convert double to int */
 else
 printf("error: zero divisor\n");
 break;
 case '?': /* print top elements of the stack */
 state = POPOFF;
 printstack();
 break;
 case '$': /* duplicate the top element of the stack */
 state = POPOFF;
 push(dupstack());
 break;
 case '@':
 state = POPOFF;
 swapstack();
 break;
 case '#': /* clear the stack */
 clearstack();
 break;
 case 'z': /* use last value printed */
 push(lastval);
 break;
 case '=':
 state = POPOFF;
 for (nvars=j=0; myvars[j].name != '\0'; ++j, ++nvars) /* get length of array */
 ;
 ++nvars;
 if (uservar != '\0') { /* if user variable, define it */
 myvars[nvars-1].name = uservar;
 myvars[nvars-1].val = pop();
 myvars[nvars].name = '\0';
 myvars[nvars++].val = 0.0;
 varmenu();
 uservar = '\0';
 }
 else printf("error: no variable to define\n");
 break;
 case '\n':
 printf("NEWLINE returned to main\n");
 if (state == POPOFF) {
 state = POPON;
 break;
 }
 else printf("\t%.8g\n", lastval = pop());
 break;
 default:
 printf("error: unknown command \"%s\"\n", s);
 break;
 }
 }
 return 0;
}

#define MAXVAL 100 /* maximum depth of val stack */
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */

/* push: push f onto value stack */
void push(double f)
{
 if (sp < MAXVAL)
 val[sp++] = f;
 else
 printf("error: stack full, can't push %g\n", f);
}

/* pop: pop and return top value from stack */
double pop(void)
{
 if (sp > 0)
 return val[--sp];
 else {
 printf("error: stack empty\n");
 return 0.0;
 }
}

/* global variables for getop */
int linep=0; /* position in line[] */
char *line;

/* getinput: get entire line of user input */
void getinput(void)
{
 int bytesRead; /* length of line as returned by getline() */
 size_t nbytes = 1000; /* size of line */

 line = (char *) malloc(nbytes + 1);
 bytesRead = getline(&line, &nbytes, stdin);

 if (bytesRead == -1)
 printf("error: no input received\n");
}

/* getop: get next character or numeric operand */
int getop(char s[])
{
 int i, c;
 
 if (line[linep] == '\0') { /* if end of string, get another line */
 linep = 0;
 getinput();
 }
 while (line[linep] == ' ' || line[linep] == '\t') /* remove whitespace */
 ++linep;
 s[0] = line[linep];
 c = s[0];
 s[1] = '\0';
 printf("line[%d] = %c\ns[0] = %c\n", linep, line[linep], s[0]);
 while (line[linep] != '\0')
 {
 if (!isdigit(c) && !isalpha(c) && c != '.' && c != '-') {
 ++linep;
 return c; /* not a number */
 }
 i = 0;
 if (isdigit(c)) { /* collect integer part */
 while (isdigit(s[++i] = line[++linep]))
 ;
 }
 if (c == '-') { /* test '-' unary or binary */
 while (isdigit(s[++i] = line[++linep]))
 ;
 if (!isdigit(s[i-1])) { /* no digits follow. '-' is binary operator */
 s[i] = '\0';
 return '-';
 }
 }
 if (isalpha(c)) { /* test letter as command or variable */
 while (isalpha(s[++i] = line[++linep]))
 ;
 s[i] = '\0';
 if (i > 1) { /* string is more than one character, it's a command */
 return ALPHA;
 }
 else if (i <= 1 && s[i-1] == 'z') { /* z = last value printed */
 return 'z';
 }
 else if (c != '\n') { /* single character string is variable */
 uservar = s[i-1]; /* store variable letter, deal with it later */
 return VARIABLE;
 }
 }
 if (c == '.') /* collect fraction part */
 while (isdigit(s[++i] = line[++linep]))
 ;
 s[i] = '\0';
 return NUMBER;
 }
}

void printstack(void) /* print the top elements of the stack */
{
 int i;

 if (sp > 0) {
 for (i=(sp-1); i >= 0; --i) 
 printf("\nstack element [%d] = %8g\n", i, val[i]);
 }
 else printf("error: nothing to print\n");
}

double dupstack(void) /* duplicate the top element of the stack */
{
 if (sp > 0)
 return val[sp-1];
 else printf("error: nothing to duplicate\n");
}

void swapstack(void) /* swap top two elements in the stack */
{
 int x, y;
 
 if (sp > 1) {
 x = val[sp-1];
 y = val[sp-2];
 val[sp-2] = x;
 val[sp-1] = y;
 }
 else printf("error: nothing to swap\n");
}

void clearstack(void) /* clear the stack */
{
 sp = 0; 
 val[sp] = '\0';
}

void cmd(char t[]) /* available commands */
{
 double op2;
 
 if (strcmp(t, "sin") == 0)
 push(sin(pop()));
 else if (strcmp(t, "exp") == 0)
 push(exp(pop()));
 else if (strcmp(t, "pow") == 0) {
 op2 = pop();
 push(pow(pop(), op2));
 }
 else printf("error: command unknown\n");
}

void varhandler(char t[]) /* deal with single-letter variables */
{
 int i;
 
 for (i=0; myvars[i].name != '\0'; ++i) { /* search for variable in array */
 if (t[0] == myvars[i].name) { /* if variable is defined, use value */
 push(myvars[i].val);
 break;
 }
 else uservar = t[0]; /* if variable undefined, store letter */
 }
}

void varmenu(void) /* print defined variables */
{
 int r;
 printf("\n/** Defined Variables **/\n\n");
 for (r=0; myvars[r].name != '\0'; ++r)
 printf("%5c = %g\n", myvars[r].name, myvars[r].val);
 printf("%5s = last value printed\n", "z");
}