Code:
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define CONTINUE 1
#define BREAK 0
#define STACK_SIZE 64
/* operand stack */
static double opstack[STACK_SIZE];
static double *stackptr;
/* default runtime options */
static int verbose = 0;
static int precision = 3;
/* protos */
static char *strtrim(char*);
static int parse_expression(char*);
static int parse_operand(char*, double*);
static int parse_operator(char);
static void resetstack();
char *strtrim(char *str) {
char *pch = str;
if (str == NULL || *str == '\0')
return str;
while (isspace(*pch)) pch++;
if (pch != str)
memmove(str, pch, (strlen(pch) + 1));
if (*str == '\0')
return str;
pch = (str + strlen(str) - 1);
while (isspace(*pch))
pch--;
*++pch = '\0';
return str;
}
void resetstack() {
if (stackptr == &opstack[0])
return;
if (verbose) { /* Dump individual items on the stack */
printf(":: Stack Dump :: ");
while (stackptr != &opstack[0])
printf("%.*f ", precision, *--stackptr);
putchar('\n');
} else /* Just reset the pointer */
stackptr = &opstack[0];
}
/** parse operations */
int parse_operand(char *token, double *operand) {
char *endPtr;
*operand = strtod(token, &endPtr);
if (*operand == HUGE_VAL) {
fprintf(stderr, "!! Input overflow.\n");
return 1;
}
if (token + strlen(token) != endPtr) {
fprintf(stderr, "!! Bad input: %s\n", token);
return 1;
}
return 0;
}
int parse_operator(char operator) {
double op1, op2;
op2 = *--stackptr;
op1 = *--stackptr;
if (verbose)
printf(":: %.*f ", precision, op1);
switch (operator) {
case '+': op1 += op2; break;
case '-': op1 -= op2; break;
case '*': op1 *= op2; break;
case '^': op1 = pow(op1, op2); break;
case '/': if (op2 == 0) {
fprintf(stderr, "!! Divide by zero\n");
return 1;
}
op1 /= op2;
break;
case '%': if (op2 == 0) {
fprintf(stderr, "!! Divide by zero\n");
return 1;
}
op1 = (int)op1 % (int)op2;
break;
}
if (verbose)
printf("%c %.*f = %.*f\n", operator, precision, op2, precision, op1);
if (op1 == HUGE_VAL) {
fprintf(stderr, "!! Result overflow\n");
return 1;
}
*stackptr++ = op1;
return 0;
}
int parse_precision(char *p) {
char *endPtr;
int pre;
pre = (int)strtol(p, &endPtr, 10);
if (endPtr != p + strlen(p)) {
fprintf(stderr, "!! Bad precision specified\n");
return 1;
} else {
precision = pre;
if (precision < 0) /* clamp negative numbers to 0 */
precision ^= precision;
printf(":: Precision set to %d decimal places.\n", precision);
}
return 0;
}
int parse_expression(char *expr) {
if (strlen(strtrim(expr)) == 0) /* empty string passed, we're done */
return BREAK;
char *token;
static const char *operators = "+/*-%^";
double operand;
while ((token = strsep(&expr, " \n"))) {
if (strlen(token) == 0) continue;
if (*token == ':') /* precision specified */
parse_precision(++token);
else if (strchr(operators, *token) && strlen(token) == 1) { /* operator */
if (stackptr - opstack < 2) {
fprintf(stderr, "!! Malformed expression -- too few operands.\n");
return CONTINUE;
}
if (parse_operator(*token) > 0) {
return CONTINUE;
}
} else { /* it's an operand, or it's bad input */
if (parse_operand(token, &operand) > 0) /* parsing failed on bad input */
return CONTINUE;
if (stackptr == &opstack[STACK_SIZE]) { /* stack overflow */
fprintf(stderr, "!! Stack overflow. Expression too large.\n");
return CONTINUE;
}
*stackptr++ = operand;
}
}
if (stackptr - opstack > 1)
fprintf(stderr, "!! Malformed expression -- too many operands.\n");
else if (stackptr - opstack == 1) {
printf(" = %.*f\n", precision, *--stackptr);
}
return CONTINUE;
}
int main(int argc, char *argv[]) {
char buf[BUFSIZ + 1];
if (argc > 1 && strcmp(argv[1], "-v") == 0) {
fprintf(stderr, "::Stack dumps enabled::\n");
verbose = 1;
}
stackptr = &opstack[0]; /* initialize stack */
do {
resetstack();
printf("> ");
buf[0] = '\0';
fgets(buf, BUFSIZ, stdin);
} while (parse_expression(buf));
return 0;
}
Bookmarks