The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.06 on page 79
Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value.
Solution by Bob Wightman
#include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <math.h> #include <string.h> #define MAXOP 100 #define NUMBER 0 /* 4-6 these are new for this exercise*/ #define IDENTIFIER 1 #define ENDSTRING 2 /* 4-6 end of new stuff */ #define TRUE 1 #define FALSE 0 #define MAX_ID_LEN 32 #define MAXVARS 30 /* The new additions deal with adding variables to the calculator. If the identifier is recognised as one of the supported mathematical functions then that function from the library is called. If the identifier is not one of the supported functions, even if it is a valid function from math.h it is ignored. This is a class 1 solution as it uses structures which are not introduced until Chapter 6. This allows the use of "normal" names for variables rather than the suggested single letter though any identifier is limited to 31 characters. The main changes are: 1. The introduction of two more define values (IDENTIFIER, ENDSTRING) along with associated cases in the switch statement. 2. Getop has also been changed to deal with reading in alphabetical characters and coping with the '=' sign. 3. A structure to hold the variable name and value. 4. Another case in the switch statement to deal with the '=' sign. 5. Altering the clearStack function to clear the array of structs as well as the stack. 6. The '<' operator now prints the last accessed variable. Improvements: The code could be made class 0 by the use of "parallel" arrays for the names and values rather than a struct but this would be messy and is the situation that structs were made for. The use of a binary tree together with dynamically allocated memory would allow the arbitrary limit of 30 variables to be avoided. This would still be a class 1 solution. This is exercise 4-6 from Kernighan & Ritchie, page 79. */ /* 4-6 this is new for this program */ struct varType{ char name[MAX_ID_LEN]; double val; }; /* 4-6 End of new stuff */ int Getop(char s[]); void push(double val); double pop(void); void showTop(void); void duplicate(void); void swapItems(void); /* 4-6 this is new for this program */ /* Changed clearStack(void) to clearStacks(struct varType var[])*/ void clearStacks(struct varType var[]); void dealWithName(char s[], struct varType var[]); void dealWithVar(char s[], struct varType var[]); int pos = 0; struct varType last; /* 4-6 End of new stuff */ int main(void) { int type; double op2; char s[MAXOP]; struct varType var[MAXVARS]; /* Use the new function here */ clearStacks(var); while((type = Getop(s)) != EOF) { switch(type) { case NUMBER: push(atof(s)); break; case IDENTIFIER: dealWithName(s, var); break; case '+': push(pop() + pop()); break; case '*': push(pop() * pop()); break; case '-': op2 = pop(); push(pop()- op2); break; case '/': op2 = pop(); if(op2) push(pop() / op2); else printf("\nError: division by zero!"); break; case '%': op2 = pop(); if(op2) push(fmod(pop(), op2)); else printf("\nError: division by zero!"); break; case '?': showTop(); break; case '#': duplicate(); break; case '~': swapItems(); break; case '!': clearStacks(var); break; case '\n': printf("\n\t%.8g\n", pop()); break; /* 4-6 this is new for this program */ case ENDSTRING: break; case '=': pop(); var[pos].val = pop(); last.val = var[pos].val; push(last.val); break; case '<': printf("The last variable used was: %s (value == %g)\n", last.name, last.val); break; /* 4-6 End of new stuff */ default: printf("\nError: unknown command %s.\n", s); break; } } return EXIT_SUCCESS; } #define MAXVAL 100 int sp = 0; /* Next free stack position. */ double val[MAXVAL]; /* value stack. */ /* push: push f onto stack. */ void push(double f) { if(sp < MAXVAL) val[sp++] = f; else printf("\nError: 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("\nError: stack empty\n"); return 0.0; } } void showTop(void) { if(sp > 0) printf("Top of stack contains: %8g\n", val[sp-1]); else printf("The stack is empty!\n"); } /* Alternatively: void showTop(void) { double item = pop(); printf("Top of stack contains: %8g\n", item); push(item); } */ void duplicate(void) { double temp = pop(); push(temp); push(temp); } void swapItems(void) { double item1 = pop(); double item2 = pop(); push(item1); push(item2); } /* 4-6 this is new for this program */ /* Altered to clear both the main stack and that of the variable structure */ void clearStacks(struct varType var[]) { int i; /* Clear the main stack by setting the pointer to the bottom. */ sp = 0; /* Clear the variables by setting the initial element of each name to the terminating character. */ for( i = 0; i < MAXVARS; ++i) { var[i].name[0] = '\0'; var[i].val = 0.0; } } /* a string/name may be either a maths function or a variable */ void dealWithName(char s[], struct varType var[]) { double op2; if(!strcmp(s, "sin")) push(sin(pop())); else if(!strcmp(s, "cos")) push(cos(pop())); else if (!strcmp(s, "exp")) push(exp(pop())); else if(!strcmp(s, "pow")) { op2 = pop(); push(pow(pop(), op2)); } /* Finally if it isn't one of the supported maths functions we have a variable to deal with. */ else { dealWithVar(s, var); } } /* Our identifier is not one of the supported maths function so we have to regard it as an identifier. */ void dealWithVar(char s[], struct varType var[]) { int i = 0; while(var[i].name[0] != '\0' && i < MAXVARS-1) { if(!strcmp(s, var[i].name)) { strcpy(last.name, s); last.val = var[i].val; push(var[i].val); pos = i; return; } i++; } /* variable name not found so add it */ strcpy(var[i].name, s); /* And save it to the last variable */ strcpy(last.name, s); push(var[i].val); pos = i; } /* 4-6 End of new stuff */ int getch(void); void unGetch(int); /* Getop: get next operator or numeric operand. */ int Getop(char s[]) { int i = 0; int c; int next; /* Skip whitespace */ while((s[0] = c = getch()) == ' ' || c == '\t') { ; } s[1] = '\0'; if(isalpha(c)) { i = 0; while(isalpha(s[i++] = c )) { c = getch(); } s[i - 1] = '\0'; if(c != EOF) unGetch(c); return IDENTIFIER; } /* Not a number but may contain a unary minus. */ if(!isdigit(c) && c != '.' && c != '-') { /* 4-6 Deal with assigning a variable. */ if('=' == c && '\n' == (next = getch())) { unGetch('\0'); return c; } if('\0' == c) return ENDSTRING; return c; } if(c == '-') { next = getch(); if(!isdigit(next) && next != '.') { return c; } c = next; } else { c = getch(); } while(isdigit(s[++i] = c)) { c = getch(); } if(c == '.') /* Collect fraction part. */ { while(isdigit(s[++i] = c = getch())) ; } s[i] = '\0'; if(c != EOF) unGetch(c); return NUMBER; } #define BUFSIZE 100 int buf[BUFSIZE]; int bufp = 0; /* Getch: get a ( possibly pushed back) character. */ int getch(void) { return (bufp > 0) ? buf[--bufp]: getchar(); } /* unGetch: push character back on input. */ void unGetch(int c) { if(bufp >= BUFSIZE) printf("\nUnGetch: too many characters\n"); else buf[bufp++] = c; }
Solution by Jesus Alvarez
#include <stdio.h> #include <stdlib.h> /* For atof() */ #include <math.h> #include <ctype.h> #define MAXOP 100 /* Max size of operand or operator. */ #define NUMBER '0' /* Signal that a number was found. */ #define VARIABLE '1' #define VARMAX 27 int is_first_input = 0; /* Prevents the solution from being printed on first input */ double var_array[VARMAX]; /* Contains user defined variables. */ int getop(char []); void push(double); double pop(void); double top(void); int clear(void); int swap(void); int elem(void); int dup(void); void sprnt(void); void result(void); void set_solution(void); void print_help(void); int main() { int type; int i, j; int op3; double topd; double op2; char s[MAXOP]; char tmp[MAXOP]; for (i = 0; i < VARMAX; i++) { var_array[i] = 0; } print_help(); while ((type = getop(s)) != EOF) { op3 = elem(); if (op3 == 0) { /* Only one input completed. */ is_first_input = 1; } else if (op3 > 1) { is_first_input = 0; } i = j = 0; switch (type) { case NUMBER: push(atof(s)); break; case VARIABLE: for (i = 2; s[i] != '\0'; i++){ tmp[j++] = s[i]; tmp[j] = '\0'; } var_array[s[0] - 'A'] = atof(tmp); 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: Divide by zero.\n"); } break; case '%': op3 = (int) pop(); push((int) pop() % op3); break; case 'c': if (clear()) { printf("Stack Cleared.\n"); } break; case 'p': if ((topd = top()) != 0) { printf("Top stack element: %g", topd); printf(" of %d elements.\n", elem()); } break; case 's': if (swap()) { printf("Swap successful.\n"); } break; case 'd': if (dup()) { printf("Duplication is successful.\n"); } else { printf("Error: Stack empty.\n"); } break; case 'r': sprnt(); break; case 'o': if (elem() < 2) { printf("Error: pow requires at least two "); printf("items on the stack.\n"); break; } op2 = pop(); push(pow(op2, pop())); break; case 'i': set_solution(); push(sin(pop())); result(); break; case 'y': set_solution(); push(cos(pop())); break; case 't': set_solution(); push(tan(pop())); break; case 'x': set_solution(); push(exp(pop())); break; case 'q': set_solution(); push(sqrt(pop())); break; case 'f': set_solution(); push(floor(pop())); break; case 'l': set_solution(); push(ceil(pop())); break; case 'v': for (i = 0; i < VARMAX; i++) { if (i < VARMAX-1) { printf("%c: %10.10G\n", 'A' + i, var_array[i]); } else { printf("%c: %10.10G\n", '=', var_array[VARMAX]); } } break; case 'h': print_help(); break; case '\n': result(); break; default: if ((type >= 'A' && type <= 'Z') || type == '=') { if (type != '=') { push(var_array[type - 'A']); } else { push(var_array[VARMAX]); } } else { 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. */ void push(double f) { if (sp < MAXVAL) { val[sp++] = f; } else { printf("Error: Stack full, cannot push %g\n", f); } } double pop(void) { if (sp > 0) { return val[--sp]; } else { printf("Error: Stack empty.\n"); return 0.0; } } double top(void) { if (sp > 0) { return val[sp-1]; } else { printf("Error: Stack empty.\n"); return 0.0; } } int clear(void) { if (sp > 0) { while(val[--sp] != '\0'); sp = 0; return 1; } else { printf("Error: Stack empty.\n"); return 0; } } int swap(void) { double sbuf; if (sp > 0) { sbuf = val[sp-2]; val[sp-2] = val[sp-1]; val[sp-1] = sbuf; return 1; } else { printf("Error: Stack empty.\n"); return 0; } } int elem(void) { return sp; } int dup (void) { if (sp > 0) { sp++; val[sp] = val[sp-1]; return 1; } else { return 0; } } void sprnt(void) { int count = 0; while (count < sp) { printf("%d:%10.12g\n", count+1, val[count]); count++; } } void result(void) { if (sp == 1 && is_first_input != 1) { printf("Solution: %10.20g\n", val[0]); var_array[VARMAX] = val[0]; is_first_input = 0; clear(); } } /* * Opens result() for execution. * Primarily used with the math functions because they can be used with only * one stack item. For ex, if "1 i" is entered as the first input, this * function would allow for a result to be shown and the stack cleared. */ void set_solution(void) { if (elem() >= 1) { is_first_input = 0; } } int getch(void); void ungetch(int); int getop(char s[]) { int i = 0, c; while ((s[0] = c = getch()) == ' ' || c == '\t'); s[1] = '\0'; if (isalpha(c) && c >= 'A' && c <= 'Z') { /* Collect the variable. */ for ( ; s[i] != ' ' && s[i] != '\n'; s[++i] = getch()); s[i] = '\0'; if (i > 1) { /* A properly formed variable definition. */ return VARIABLE; } else { return c; } } else if (!isdigit(c) && c != '.' && c != '-') { return c; /* Not a number. */ } if (c == '-') { if ((c = getch()) == ' ') { /* If the next char is space, then c is a operator. */ return c; } else if (isdigit(c)) { s[++i] = c; } } if (isdigit(c)) { /* Collect integer part. */ while (isdigit(s[++i] = c = getch())); } if (c == '.') { /* Collect fraction part. */ while (isdigit(s[++i] = c = getch())); } s[i] = '\0'; if (c != EOF) { ungetch(c); } return NUMBER; } #define BUFSIZE 100 char buf[BUFSIZE]; /* Buffer for ungetch. */ int bufp = 0; /* Next free position in buf. */ int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } void ungetch(int c) { if (bufp >= BUFSIZE) { printf("Ungetch: Too many characters.\n"); } else { buf[bufp++] = c; } } void print_help(void) { printf("The Polish Calculator\n"); printf("-----------------------------------------------\n"); printf("-> Enter equations in the form: \"1 1 + 2 5 + *\"\n"); printf("-> Use \"A=1 B=2 C=3\" to store variables.\n"); printf("-> Use \"A B C * *\" to use stored variables.\n"); printf("-----------------------------------------------\n"); printf(">>> Command Help:\n"); printf(">>> c: Clear memory.\n"); printf(">>> p: Print last character.\n"); printf(">>> s: Swap last two characters.\n"); printf(">>> d: Duplicate the last input.\n"); printf(">>> r: Print the entire stack.\n"); printf(">>> v: Print variable list.\n"); printf(">>> o: pow(x,y), x^y, x > 0.\n"); printf(">>> i: sin(x), sine of x.\n"); printf(">>> y: cos(x), cosine of x.\n"); printf(">>> t: tan(x), tangent of x.\n"); printf(">>> x: exp(x), e^x, exponential function.\n"); printf(">>> q: sqrt(x), x >= 0, square of x.\n"); printf(">>> f: floor(x), largest integer not greater than x.\n"); printf(">>> l: ceil(x), smallest integer not less than x.\n"); printf(">>> =: Access the last successful solution.\n"); printf(">>> h: Print this help text.\n"); }
Solution by menonsahab
/* I'm beginning to feel that the questions in K&R are getting more and more vague. If you see the above answers you'll see that what Jesus understood from the question is totally different from what Bob did. I am personally of the opinion that what Jesus has implemented isn't really what the question meant. Bob's answer is great. I've tried to implement a category 0 solution. It does what Bob's code does except: 1) It only handles single letter variable names a,b,c,...,x,y,z 2) It does not handle '=' */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #define MAXOP 100 #define NUMBER 0 #define MAXVAL 100 #define BUFSIZE 100 #define IDENTIFIER 1 #define VARIABLE 2 int getop(char *); void push(double); double pop(void); int getch(void); void ungetch(int); void viewstack(void); void showTop(void); void swap(void); void duplicate(void); void clearStack(void); void mathfunc(char *); char last; double variableValue[26] = {0}; int bufp = 0; char buf[BUFSIZE]; int sp = 0; double val[MAXVAL]; void push(double f) { if(sp < MAXVAL) val[sp++] = f; else printf("error: stack full, can't push %g\n", f); } double pop(void) { if(sp > 0) return val[--sp]; else { printf("error: stack empty\n"); exit(1); //return 0.0; } } int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } void ungetch(int c) { if(bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } int getop(char *s) { int i, c, d, flag, len; while((s[0] = c = getch()) == ' ' || c == '\t'); s[1] = '\0'; if(!isalnum(c) && c != '.' && c != '-') return c; if(c == '-') { d = getch(); if(d == ' ') return c; else ungetch(d); } i = 0; if(isalnum(c) || c == '-') while(isalnum(s[++i] = c = getch())); if(c == '.') while(isalnum(s[++i] = c = getch())); s[i] = '\0'; if(c != EOF) ungetch(c); flag = 1; len = strlen(s); if(len == 1 && isalpha(s[0])) { last = s[0]; return VARIABLE; } for(i = 0; i < len; i++) if(!isalpha(s[i])) { flag = 0; break; } if(flag == 1) return IDENTIFIER; else return NUMBER; } void viewstack(void) { int i; printf("\nstack:\n"); for(i = sp - 1; i >= 0; i--) printf("%lf\n", val[i]); } void showTop(void) { sp > 0 ? printf("\t%.8g\n", val[sp-1]) : printf("stack is empty\n"); } void swap(void) { double temp; if(sp < 1) printf("error: stack has less than 2 elements, can't swap\n"); else { temp = val[sp - 1]; val[sp - 1] = val[sp - 2]; val[sp - 2] = temp; } } void duplicate(void) { if(sp > MAXVAL - 1) printf("error: stack is full, can't duplicate\n"); else { double temp = pop(); push(temp); push(temp); ++sp; } } void clearStack(void) { sp = 0; } void mathfunc(char *s) { if(strcmp(s, "sin") == 0) { if(sp < 1) printf("error: stack is empty, can't use sin function\n"); else push(sin(pop())); } else if(strcmp(s, "cos") == 0) { if(sp < 1) printf("error: stack is empty, can't use cos function\n"); else push(cos(pop())); } else if(strcmp(s, "exp") == 0) { if(sp < 1) printf("error: stack is empty, can't use exp function\n"); else push(exp(pop())); } else if(strcmp(s, "pow") == 0) { if(sp < 2) printf("error: stack has less than 2 elements, can't use pow function\n"); else { double op2; op2 = pop(); push(pow(pop(), op2)); } } else printf("%s is not a supported function\n", s); } int main() { int type; double op2; char s[MAXOP]; while((type = getop(s)) != EOF) { //viewstack(); // Use this function if you wish to see the stack after every iteration switch(type) { case NUMBER: push(atof(s)); break; case IDENTIFIER: mathfunc(s); break; case VARIABLE: push(variableValue[last - 97]); break; case '_': // This prints the most recent variable push(0); // So that when \n is encountered next, the stack isn't empty printf("%c\n", last); 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 '%': op2 = pop(); if(op2 != 0.0) push(fmod(pop(), op2)); else printf("error: division by zero\n"); break; case '?': // show top item on stack without popping showTop(); break; case '~': // swap top two elements of the stack swap(); break; case '#': // duplicate the top element duplicate(); break; case '!': // clearStack clearStack(); break; case '\n': printf("\t%.8g\n", pop()); break; default: printf("error: unknown command %s\n", s); break; } //viewstack(); } return 0; }
Solution by Luke Panayi
This could be cleaned up a bit more by modulating the code with more functions, but the logic is all there. I see menonsahab had the same idea as me for how to more elegantly handle 26 single letter variables while still maintaining a category 0 solution. Unlike their solution though, this one does handle '=' characters.
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <math.h> #include <string.h> #define MAXOP 100 #define NUMBER '0' #define MAXVAL 100 #define BUFSIZE 100 #define VAR '_' int getop(char []); void push(double); double pop(void); int getch(void); void ungetch(int); int sp = 0; double val[MAXVAL]; void push(double f) { if (sp < MAXVAL) val[sp++] = f; else printf("error: stack full, can't push %g\n", f); } double pop(void) { if (sp > 0) return val[--sp]; else { printf("error: stack empty\n"); return 0.0; } } void peek(void) { if (sp > 0) { printf("%g\n", val[sp]); } else { printf("error: stack empty\n"); } } void swap(void) { if (sp > 1) { double tmp; tmp = val[sp]; val[sp] = val[sp-1]; val[sp-1] = tmp; } else { printf("error: not enough elements in stack\n"); } } void clear(void) { sp = 0; //should be enough to simply set the pointer to 0, old values will be overwritten. } void duplicate(void) { if (sp < MAXVAL -1) { push(val[sp]); } else { printf("error: stack full\n"); } } char variable = ' '; //initially as space such that malformed use of '=' won't cause segfaults int asignment = 0; //hacky way to not push a variable to stack before it's been defined int getop(char s[]) { int i, c; while ((s[0] = c = getch()) == ' ' || c == '\t'); s[1] = '\0'; if (!isdigit(c) && !isalpha(c) && c != '.' && c != '-') return c; i = 0; if (isalpha(c)) { while (isalpha(s[++i] = c = getch())); s[i] = '\0'; if (i == 1) { //s is a variable variable = s[0]; if ((c=getch()) == '=') asignment = 1; ungetch(c); return VAR; } if (strcmp(s, "sin")) //we could had done all the math.h functions but, effort return 1; if (strcmp(s, "cos")) return 2; if (strcmp(s, "tan")) return 3; if (strcmp(s, "exp")) return 4; printf("error: unsupported function or malformed input\n"); return '\n'; } if (c == '-') { s[i] = c; if ((c = getch()) == ' ') { return '-'; } else s[++i] = c; } if (isdigit(c)) while (isdigit(s[++i] = c = getch())); if (c == '.') while (isdigit(s[++i] = c = getch())); s[i] = '\0'; if (c != EOF) ungetch(c); return NUMBER; } char buf[BUFSIZE]; int bufp = 0; int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } void ungetch(int c) { if (bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } double variables[26]; void define(char var, double n) { var = tolower(var); if (var >= 97 && var <= 122) variables[tolower(var)-97] = n; else printf("error: variable symbol out of range\n"); } double variableLookUp(char var) { if (var >= 97 && var <= 122) return variables[tolower(var)-97]; else printf("error: variable symbol out of range\n"); return 0.0; } int main() { int type; double op2, lastPrinted; char s[MAXOP]; while ((type = getop(s)) != EOF) { switch (type) { case NUMBER: push(atof(s)); break; case '+': push(pop() + pop()); break; case '*': push(pop() * pop()); break; case '-': op2 = pop(); push(pop() - op2); break; case '/': if (op2 != 0.0) { op2 = pop(); push(pop() / op2); } else printf("error: zero divisor\n"); break; case '%': op2 = pop(); if (op2 != 0.0) { push(fmod(pop(), op2)); } else printf("error: zero divisor\n"); break; case '^': op2 = pop(); push(pow(pop(), op2)); break; case 1: //such that variables can be used, using non-printable characters for math functions push(sin(pop())); break; case 2: push(cos(pop())); break; case 3: push(tan(pop())); break; case 4: push(exp(pop())); break; case '=': define(variable, pop()); break; case VAR: if (asignment == 0) push(variableLookUp(variable)); else asignment = 0; break; case '\n': lastPrinted = pop(); printf("\t%.8g\n", lastPrinted); break; } } return 0; }
Solution by anonymous
I added 26 variables which are lowercase letters. You set them by inputting <letter>=<value>. I reserved uppercase L for the variable that contains the most recently printed value. I interrupted the most recently printed as the final result printed, not when something was printed from another command. The varVals array contains the value of a variable. A basic way of using the variables is as follows:
i=7 i 2 * 14 L 10 + 24 L i + 31
#include <stdio.h> #include <stdlib.h> // for atof() #include <ctype.h> #include <math.h> #include <string.h> // for strlen() /* Exercise 4-6. Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value. */ #define MAXOP 100 // max size of operand or operator #define NUMBER '0' // signal that a number was found #define SETVARIABLE 's' // signal that a variable is being assigned #define GETVARIABLE 'g' // signal that a variable is being retrieved #define ERRORSIGNAL ' ' // signal that an error occurred in getop #define MAXVAL 100 // maximum depth of val stack #define BUFSIZE 100 // buffer size for getch and ungetch #define NUMVARS 27 // number of variables supported (a-z) plus the variable L for last printed enum boolean {FALSE, TRUE}; int sp = 0; // next free stack position double val[MAXVAL]; // value stack char buf[BUFSIZE]; // buffer for ungetch int bufp = 0; // next free position in buf double varVals[NUMVARS] = {0.0}; // array to store the double values for variables. Supports variables a through z (lower case). Initial value is 0 int getop(char s[]); void push(double f); double pop(void); int getch(void); void ungetch(int c); void printTop(void); void duplicateTop(void); void swapTopTwo(void); // reverse Polish calculator // note: convert ((((-1 - 2) * (4 + -5)) / -3) % 5) * (-1 - -10) to -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * for reverse Polish notation. -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * == -9 int main() { int type; double op2; char s[MAXOP]; char skipNextNewline = FALSE; while ((type = getop(s)) != EOF) { switch (type) { case NUMBER: push(atof(s)); // convert the string to type double and push it on the stack break; case SETVARIABLE: if (strlen(s) > 2 && s[1] == '=') { int v = s[0]; // stores variable int i = 1; // start at 1 since while loop needed ++i for a few reasons while (s[++i] != '\0') // this removes the variable name= part of s e.g. if s == "a=123.45" after loop s == "123.45" s[i - 2] = s[i]; // shifts chars two to the left by 2 s[i - 2] = '\0'; // since '\0' isn't copied, terminate string manually varVals[v - 'a'] = atof(s); // convert string to double and store it in array } else printf("error: set variable length too small or incorrectly formatted (%s)\n", s); skipNextNewline = TRUE; break; case GETVARIABLE: push(varVals[s[0] - 'a']); // convert the variable name to stored value break; case '+': push(pop() + pop()); // pop last two digits to sum them and push the result on the stack break; case '*': push(pop() * pop()); // pop last two digits to multiply them and push the result on the stack break; case '-': /* Because + and * are commutative operators, the order in which the popped operands are combined is irrelevant, but for - and / the left and right operands must be distinguished. In push(pop() - pop());, the order in which the two calls of pop are evaluated is not defined. To guarantee the right order, it is necessary to pop the first value into a temporary variable. Hence op2 = pop() in - and / but not in + and * */ op2 = pop(); push(pop() - op2); // pop last two digits to subtract them in the correct order and push the result on the stack break; case '/': op2 = pop(); if (op2 != 0.0) push(pop() / op2); // pop last two digits to divide them in the correct order and push the result on the stack else printf("error: zero divisor\n"); break; case '%': op2 = pop(); if (op2 != 0.0) push(fmod(pop(), op2)); // pop last two digits in the correct order to find the modulus and push the result on the stack else printf("error: zero divisor\n"); break; case '?': printTop(); skipNextNewline = TRUE; break; case '#': duplicateTop(); skipNextNewline = TRUE; break; case '~': swapTopTwo(); skipNextNewline = TRUE; break; case '!': // sets next free stack position to zero (meaning the value stack is empty). // all of the original values are still there, but they will no longer be accessible by the current functions and they will be overwritten when new elements are stored sp = 0; skipNextNewline = TRUE; break; case '$': push(sin(pop())); skipNextNewline = TRUE; break; case '@': push(exp(pop())); skipNextNewline = TRUE; break; case '^': op2 = pop(); push(pow(pop(), op2)); // pop last two digits in the correct order to raise a number to the given power skipNextNewline = TRUE; break; case '&': push(sqrt(pop())); skipNextNewline = TRUE; break; case ',': push(fabs(pop())); skipNextNewline = TRUE; break; case 'L': push(varVals[NUMVARS - 1]); // adds the last printed value to the stop of the stack break; case '\n': if (skipNextNewline) skipNextNewline = FALSE; else { varVals[NUMVARS - 1] = pop(); // updates last printed value printf("\t%.8g\n", varVals[NUMVARS - 1]); // get the final result } break; default: printf("error: unknown command %s\n", s); break; } } return 0; } // push: push f onto value stack void push(double f) { if (sp < MAXVAL) // if value stack still has space, add f 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) // if the next free stack position is greater than zero, return the highest level item from stack return val[--sp]; else { printf("error: stack empty\n"); return 0.0; } } // getop: get next operator or numeric operand int getop(char s[]) { int i, c; char setVar = FALSE; i = 0; while ((s[0] = c = getch()) == ' ' || c == '\t') // skip white space ; s[1] = '\0'; // terminate string in case input is not a number (s is expected to be a string throughout program) if (c >= 'a' && c <= 'z') { if ((s[++i] = c = getch()) == '=') // get next char and check if it was an equal symbol. Update s in case of error setVar = TRUE; else if (c == ' ' || c == '\t' || c == '\n') { ungetch(c); // return the whitespace since it will be processed later return GETVARIABLE; } if (!(setVar && ((s[++i] = c = getch()) == '-' || isdigit(c)))) return ERRORSIGNAL; // triggers an error and will display what is in s } if (!isdigit(c) && c != '.' && c != '-') return c; // not a number. Probably an operator, so return it. Minus operator is a special case and is handled right before return NUMBER; if (c == '-' || isdigit(c)) // collect integer(s), if any, after first digit found or after minus symbol found while (isdigit(s[++i] = c = getch())) ; if (c == '.') // collect fraction part if period is found while (isdigit(s[++i] = c = getch())) ; s[i] = '\0'; // terminate string after digits were captured if (c != EOF) ungetch(c); // since we read to far, push the last read char back on the getch buffer. This buffer is read first before getting the next char from input if (i == 1 && s[0] == '-') // if s[0] == '-' && s[1] == '\0', return minus operator return '-'; if (setVar) return SETVARIABLE; else if (c >= 'a' && c <= 'z') return ERRORSIGNAL; // if last char is a variable, throw error return NUMBER; } // get a (possibly pushed back) character // checks to see if there are any chars in buffer. If there are, get those and return it. If not, call getchar() from stdio.h to get next char from input int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } // push character back on input // if bufp is less than BUFSIZE, there is room to store more chars to be read by getch next and it stores c and updates the index for it void ungetch(int c) { if (bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } // prints the top element in the value stack void printTop(void) { if (sp > 0) printf("\t%.8g\n", val[sp - 1]); else printf("error: stack empty\n"); } void duplicateTop(void) { if (sp < MAXVAL) // only need to see if there is space for one more push(val[sp - 1]); // duplicates top item else printf("error: stack full, can't duplicate top element\n"); } void swapTopTwo(void) { // if sp == 2, there are at least two elements stored if (sp > 1) { // <third> <second> <first> double first = pop(); // <third> <second> double second = pop(); // <third> push(first); // <third> <first> push(second); // <third> <first> <second> } else printf("error: can't swap top two, not enough elements\n"); }