/* RPN Calculator,
My suggested soultions to the Exercises in
"The C Programming Language" aka "K&R"
Exercises 4-3 through 4-10
Author (of code below): Joseph Bleau
Date: September 12th, 2011
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define STACKSIZE 128
#define LINESIZE 128
#define TOKENSIZE 32
#define SERRORSIZE 32
#define VARSTACKSIZE 25
#define _DEBUG_ 0
#define PDEBUGS(x) do{if(_DEBUG_)printf("%s",x);}while(0)
#define PDEBUGC(x) do{if(_DEBUG_)printf("%c",x);}while(0)
#define PDEBUGI(x) do{if(_DEBUG_)printf("%d",x);}while(0)
enum ERROR{ SUCCESS = 0, ALPHA_CHAR_FOUND,
EXCEEDED_MAX_TOKENSIZE,
EXCEEDED_MAX_STACKSIZE,
DIVIDE_BY_ZERO} errorno;
int sgetline(char[], int);
int isoperator(char);
int isspace(char);
int isdigit(char);
void geterror(char[], int);
void view_stack(int*, int);
void print_vars(int[], int);
void show_commands();
int main(int argc, char* argv[])
{
int i, j; /* loop counter */
int val; /* stores the outcome of an operation */
/* Input buffer */
char line[LINESIZE];
int len = 0;
/* Our operand stack. */
int stack[STACKSIZE];
int stack_ptr = -1;
int tmp;
int single = 0;
/* Our Variable Stack */
int varstack[VARSTACKSIZE];
int varptr = 0;
/* holds current token being worked with */
char token[TOKENSIZE];
/* holds ascii representation of error */
char serror[SERRORSIZE];
/* Clean errorno state */
errorno = 0;
for( i = 0; i < VARSTACKSIZE; i++)
varstack[i] = 0xDEADBEEF;
/* Take input until a newline is receieved, and when it is,
attempt to parse the line. */
while( errorno == SUCCESS &&
( printf("stack(%d): ", stack_ptr+1),
len = sgetline(line, LINESIZE)) &&
strcmp(line,"quit") != 0 )
{
/* Check for possible commands */
if( strcmp(line, "view_stack") == 0){
view_stack(stack, stack_ptr);
continue;
}
if( strcmp(line, "swap_top") == 0){
tmp = stack[stack_ptr-1];
stack[stack_ptr-1] = stack[stack_ptr];
stack[stack_ptr] = tmp;
printf("top two items in stack were swapped\n");
continue;
}
if (strcmp(line, "help") == 0){
show_commands();
continue;
}
if(strcmp(line, "empty") == 0){
stack_ptr = -1;
continue;
}
if(strcmp(line, "dupe_top") == 0){
stack[++stack_ptr] = stack[stack_ptr-1];
continue;
}
if(strcmp(line, "print_vars") == 0){
print_vars(varstack, VARSTACKSIZE);
continue;
}
if(len == STACKSIZE+1){
show_commands();
continue;
}
/* Greedy eater, munch up digits until a
space or operator is found, and then place it on
our stack */
for (single = val = i = j = 0; i < len; ++i){
if(!isdigit(line[i]) &&
!isoperator(line[i]) &&
!isspace(line[i])){
if( line[i] >= 'a' && line[i] <= 'z'){
varptr = line[i] - 'a';
if( varstack[varptr] != 0xDEADBEEF ){
stack[++stack_ptr] = varstack[varptr];
j = 0;
}
}
else{
printf("Error: invalid command.\n");
break;
}
}
else if(isspace(line[i]))
{
if(j == 0)
continue;
token[j] = '\0';
PDEBUGS("DEBUG: attempting to stack token: ");
PDEBUGS(token);
PDEBUGC('\n');
if(stack_ptr + 1 >= STACKSIZE){
errorno = EXCEEDED_MAX_STACKSIZE;
break;
}
stack[++stack_ptr] = atoi(token);
j = 0;
}
else if(isoperator(line[i]))
{
PDEBUGS("DEBUG: operator found, stack size: ");
PDEBUGI(stack_ptr);
PDEBUGC('\n');
if(stack_ptr > 0 || line[i] == '=' ||
line[i] == '_' || line[i] == 's' || line[i] == ':' ){
switch(line[i]){
case 's':
if(stack_ptr > -1 && stack_ptr % 2 == 0){
stack[stack_ptr] = sin(stack[stack_ptr]);
single = 1;
}
else if(stack_ptr == -1){
printf("Error: Not enough operands\n");
single = 1;
break;
}
break;
case ':':
if(stack_ptr > -1 && stack_ptr % 2 == 0){
varstack[varptr] = stack[stack_ptr];
stack_ptr--;
single = 1;
}
else if(stack_ptr == -1){
printf("Error: Not enough operands\n");
single = 1;
break;
}
break;
case '%':
val = stack[stack_ptr] % stack[stack_ptr-1];
break;
case '+':
val = stack[stack_ptr] + stack[stack_ptr-1];
break;
case '-':
val = stack[stack_ptr] - stack[stack_ptr-1];
break;
case '_':
if(stack_ptr > -1 && stack_ptr % 2 == 0){
stack[stack_ptr] *= -1;
single = 1;
}
else if(stack_ptr == -1){
printf("Error: Not enough operands\n");
single = 1;
break;
}
break;
case '*':
val = stack[stack_ptr] * stack[stack_ptr-1];
break;
case '/':
if(stack[stack_ptr-1] == 0){
errorno = DIVIDE_BY_ZERO;
break;
}
val = stack[stack_ptr] / stack[stack_ptr-1];
break;
case '=':
if(stack_ptr == 0 && errorno == SUCCESS){
printf("Outcome: %d\n", stack[0]);
stack_ptr--;
}
else if(stack_ptr == -1)
printf("Error: no value to show\n");
else
printf("Error: excess items on stack, cannot apply '='\n");
break;
}
if(line[i] != '=' ){
if(!single)
stack[--stack_ptr] = val;
}
}
else{
printf("Error: Not enough operands\n");
break;
}
}
else
{
if(j+1 > TOKENSIZE){
errorno = EXCEEDED_MAX_TOKENSIZE;
break;
}
token[j++] = line[i];
}
}
if(j > 0){
token[j] = '\0';
if(stack_ptr + 1 >= STACKSIZE){
errorno = EXCEEDED_MAX_STACKSIZE;
break;
}
stack[++stack_ptr] = atoi(token);
j = 0;
}
}
geterror(serror,SERRORSIZE);
printf("Exiting: %s", serror);
return errorno;
}
int isoperator(char c){
if(c == '+' || c == '-' || c == '*' ||
c == '/' || c == '=' || c == '%' ||
c == '_' || c == 's' || c == ':')
return 1;
return 0;
}
int isspace(char c){
if( c == '\t' || c == ' ')
return 1;
return 0;
}
int sgetline(char line[], int bufsize)
{
int i, c;
i = 0;
while( (c = getchar()) != '\n' && c != EOF ){
if(i < bufsize)
line[i++] = c;
else
break;
}
line[i] = '\0';
if(i == 0)
return STACKSIZE+1;
return i;
}
void view_stack(int* stack, int stack_ptr)
{
int i;
printf("Our stack:\n");
for (i = 0; i <= stack_ptr; i++)
printf("\t%d\n", stack[i]);
if(i == 0)
printf("\t(empty)\n");
}
void print_vars(int varstack[], int bufsize)
{
int i,c = 0;
printf("variable list:\n");
for( i = 0; i < bufsize; i++)
if( varstack[i] != 0xDEADBEEF){
printf("\t%c: %d\n", i + 'a', varstack[i]);
c++;
}
if(!c)
printf("\t(empty)\n");
}
void show_commands()
{
printf("Valid commands:\n");
printf("\thelp\n\tview_stack\n\tswap_top\n");
printf("\tdupe_top\n\tempty\n\tquit\n");
printf("\n\noperators: +, -, *, /, _, %, s\n\n");
}
void geterror(char serror[], int bufsize)
{
if(bufsize < 20)
return;
switch(errorno){
case SUCCESS:
strcpy(serror,"SUCCESS");
break;
case ALPHA_CHAR_FOUND:
strcpy(serror,"ALPHA_CHAR_FOUND");
break;
case EXCEEDED_MAX_TOKENSIZE:
strcpy(serror,"EXCEEDED_MAX_TOKENSIZE");
break;
case DIVIDE_BY_ZERO:
strcpy(serror, "DIVIDE_BY_ZERO");
break;
case EXCEEDED_MAX_STACKSIZE:
strcpy(serror, "EXCEEDED_MAX_STACKSIZE");
break;
default:
strcpy(serror,"UNKOWN_ERROR");
break;
}
}