/******************************************************************* * RPN-laskin, versio 4 * * Laskee RPN-muodossa annettuja lausekkeita * esim. * 2 3 4 + * ---> 2*(3+4) * "x5+6*sin" ---> sin((x+5)*6) * "x 9 + y * sqrt 2 +" ---> sqrt((x+9)*y) + 2 * * Tuloksesta voi jatkaa seuraavalla rivillä, * joka rivin jälkeen tulostetaan pinoon jääneet arvot, * päällimmäinen (uusin) oikealla. * Laskenta lopetetaan syötön loppuessa (ctrl-d, tiedoston loppu). * * Muuttujan arvoksi voi asettaa pinon päällimmäisen arvon syntaksilla =var: * "5 =x 6 =y x y *" ---> x=5, y=6, x*y * (muuttujien arvoja ei kysellä). * * * Sallittuja muuttujanimiä kaikki kirjaimista a-z muodostuvat, * jotka eivät ole mitään tunnettuja funktioita: * sin, cos, tan, asin, acos, atan, sqrt * * Käännös edellyttää matematiikkakirjaston linkitystä mukaan_ * * cc -o rpncalc rpncalc.c -lm *******************************************************************/ #include #include #include #include #define MAXLINE 100 #define MAXOPS 100 #define MAXDEPTH 6 #define MAXVARNAME 10 static double stack[MAXDEPTH]; double *stkp = stack; void printstack(void) { double *s = stack; printf("["); while (s < stkp) { printf(" %g", *s); ++s; } printf(" ]\n"); } double add(double x, double y) { return x+y; } double subtract(double x, double y) { return x-y; } double multiply(double x, double y) { return x*y; } double divide(double x, double y) { return x/y; } double (*opers[])(double,double) = { add, subtract, multiply, divide }; char optable[] = "+-*/"; struct { double (*fun)(double); char *name; } funcs[] = { { sin, "sin" }, { cos, "cos" }, { tan, "tan" }, { asin, "asin" }, { acos, "acos" }, { atan, "atan" }, { sqrt, "sqrt" }, { 0, 0 } }; struct vars { char name[MAXVARNAME]; double value; } variables[MAXLINE] = { {"pi", M_PI}, {"e", M_E} }; static int variablecount = 1; /* highest index in use */ static int constantlimit = 1; /* last predefined constant */ typedef struct { enum { END, CONST, VAR, SET, FUN1, FUN2 } type; union { double value; double *var; double (*f1)(double); double (*f2)(double, double); } u; } action; double evaluate(action *prog) { for ( ; ; ) { switch(prog->type) { case CONST: if (stkp >= stack+MAXDEPTH) { fprintf(stderr, "ERROR: stack overflow\n"); exit(1); } *stkp++ = prog->u.value; break; case VAR: if (stkp >= stack+MAXDEPTH-1) { fprintf(stderr, "ERROR: stack overflow\n"); exit(1); } *stkp++ = *(prog->u.var); break; case SET: if (stkp == stack) { fprintf(stderr, "ERROR: stack underflow\n"); exit(1); } *(prog->u.var) = *--stkp ; break; case FUN1: *(stkp-1) = prog->u.f1(*(stkp-1)); break; case FUN2: if (stkp == stack) { fprintf(stderr, "ERROR: stack underflow\n"); exit(1); } --stkp; *(stkp-1) = prog->u.f2(*(stkp-1), *stkp); break; case END: return *(stkp-1); } prog++; } } int find_fun(char *name) { int i=-1; do { if (!(funcs[++i].name)) return -1; } while (strcmp(name,funcs[i].name)); return i; } int find_var(char *name) { int i=-1; do { if (++i > variablecount) { strcpy(variables[i=++variablecount].name, name); return i; } } while (strcmp(name,variables[i].name)); return i; } int compile(char *expr, action *prog) { double num; char ch, *name; int i, set; for (;*expr; expr++) { set=0; switch(ch=*expr) { case ' ': case '\n': case '\t': continue; case '+': case '-': case '*': case '/': prog->u.f2=opers[strchr(optable,ch)-optable]; prog++->type=FUN2; continue; case '=': set=1; ch=*++expr; break; } if ('a'<=ch && ch<='z') { name=expr; do { ch = *++expr; } while ('a'<=ch && ch<='z'); *expr='\0'; if ((i=find_fun(name))>=0) { if (set) { fprintf(stderr, "Cannot assign to a function!\n"); return 1; } *expr--=ch; prog->u.f1=funcs[i].fun; prog++->type=FUN1; continue; } else { i = find_var(name); *expr--=ch; prog->u.var= &variables[i].value; if (set) { if (i<=constantlimit) { fprintf(stderr, "Cannot assign to a predefined constant!\n"); return 1; } prog++->type=SET; } else prog++->type=VAR; continue; } } if (('0'<=ch && ch<='9') || ch=='-' || ch=='.') { name=expr; do { ch = *++expr; } while (('0'<=ch && ch<='9') || ch=='.' || ch=='e'); *expr='\0'; sscanf(name, "%lf", &num); *expr--=ch; prog->u.value=num; prog++->type=CONST; continue; } printf("Invalid character '%c'\n", ch); return 1; } prog->type=END; return 0; } int main() { char expr[MAXLINE+1]; action prog[MAXOPS]; while (printf("? "), fgets(expr, MAXLINE, stdin) || !feof(stdin)) { if (!compile(expr, prog)) { evaluate(prog); printstack(); } } return 0; }