diff options
-rw-r--r-- | ch4/4-10_getline-calc.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/ch4/4-10_getline-calc.c b/ch4/4-10_getline-calc.c new file mode 100644 index 0000000..499e71a --- /dev/null +++ b/ch4/4-10_getline-calc.c @@ -0,0 +1,276 @@ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <math.h> +#include <string.h> + +/* 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; +} |