#include #include #include #include #include /* The C Programming Language: 2nd Edition * * Exercise 4-10: An alternate organization uses getline() to read an entire * input line; this makes getch() and ungetch() unnecessary. Revise the * calculator to use this approach. * * Answer: This is much trickier than it seems; it teaches you how to refactor * a program. You won't be using the built-in getline() for this, but rather * the function that we wrote back in Chapter 1. * * The majority of the trickiness lies in getop(). The idea is to store the * entire line in a buffer that sits in an external variable. Then you pick and * choose which chars to put into your target string, in much the same way we * picked and chose which characters to getch() previously. * * The rest of the program works about the same, with only a few minor tweaks * needed. The cool part is the whole thing didn't need to be rewritten; just * the "plumbing" part, getop(). * * In addition to refactoring, this entire project demonstrates the usefulness * of modularizing your code into small, discrete functions. */ #define MAXOP 100 #define NUMBER '0' #define MAXVAL 100 #define BUFSIZE 100 int getop(char []); void push(double); double pop(void); void stack_top(void); double dupe_top(void); void swap_top_two(void); void clear_stack(void); double fetch_var(char); void store_var(char, double); void store_last(double); double fetch_last(void); int mygetline(char [], int); int sp = 0; // Next free stack position double val[MAXVAL]; // Value stack char buf[BUFSIZE]; // buffer int bufp = 0; // next free position in buf double vars[27]; /* Reverse Polish calculator: * * Binary operations (+-*\%) * operand operand operator * * Example: 6 minus 2 in Reverse Polish Notation is "6 2 -" */ int main() { int type; double op2; char s[MAXOP]; char ltr; while (mygetline(buf, BUFSIZE) != 0) { bufp = 0; while ((type = getop(s)) != '\0') { if (isalpha(type) && islower(type)) { push(fetch_var(type)); continue; } 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 '/': op2 = pop(); if (op2 != 0.0) { push(pop() / op2); } else { printf("Error: Cannot divide by zero.\n"); } break; /* Yay for modulus! */ case '%': op2 = pop(); if (op2 != 0.0) { push((int)pop() % (int)op2); } else { printf("Error: Cannot modulo by zero.\n"); } break; /* Top of stack */ case '?': stack_top(); break; /* Dupe the top of the stack */ case '#': dupe_top(); break; /* Swap the top two */ case '~': swap_top_two(); break; /* Clear the stack */ case '!': clear_stack(); break; /* sin() support */ case '(': op2 = sin(pop()); push(op2); break; /* exp() support */ case '{': op2 = exp(pop()); push(op2); break; /* pow() support */ case '^': op2 = pop(); push(pow(pop(), op2)); break; /* 'lastprint' support */ case '@': push(fetch_last()); break; /* setting variables */ case '=': ltr = buf[bufp]; if (isalpha(ltr) && islower(ltr)) { op2 = pop(); store_var(ltr, op2); push(op2); } else { push(0.0); } break; /* Final output */ case '\n': op2 = pop(); printf("\t%.8g\n", op2); /* Extra Credit: Lets output every non-zero variable! */ for (ltr = 'a'; ltr <= 'z'; ltr++) { if (fetch_var(ltr) != 0) { printf("\t%c: %.8g\n", ltr, fetch_var(ltr)); } } store_last(op2); break; default: printf("Error: Unknown command %s\n", s); break; } } } return 0; } 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; } } int getop(char s[]) { int i, c; while ((s[0] = c = buf[bufp++]) == ' ' || c == '\t'); if (c >= 'a' && c <= 'z') { return c; } s[1] = '\0'; /* The final check is for negative numbers. */ if (!isdigit(c) && c != '.' && c != '-') { return c; } i = 0; /* The second half of this if-statement accounts for negatives */ if (c == '-') { if (!isdigit(buf[bufp]) && buf[bufp] != '.') { return c; } else { s[i] = c; c = buf[bufp]; } } else { s[i] = c; } if (isdigit(c)) { while (isdigit(s[++i] = c = buf[bufp++])); } if (c == '.') { while (isdigit(s[++i] = c = buf[bufp++])); } s[i] = '\0'; bufp--; return NUMBER; } void stack_top(void) { if (sp > 0) { printf("Top of stack is %8g\n", val[sp - 1]); } } double dupe_top(void) { double temp = pop(); push(temp); push(temp); } void swap_top_two(void) { double tmp1, tmp2; tmp1 = pop(); tmp2 = pop(); push(tmp1); push(tmp2); } void clear_stack(void) { sp = 0; } double fetch_var(char c) { return vars[c - 'a']; } void store_var(char c, double f) { vars[c - 'a'] = f; } void store_last(double f) { vars[26] = f; } double fetch_last(void) { return vars[26]; } int mygetline(char s[], int lim) { int c, i = 0; while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { s[i++] = c; } if (c == '\n') { s[i++] = c; } s[i] = '\0'; return i; }