/* contributed by demoncrat@efnet.#c on march 2001. */ /* edited by megaton. added double precision numbers */ #include #include #include #include #include "dcalc.h" static const char *complaint = NULL; static void complain (const char *msg) { if (complaint == NULL) complaint = msg; } enum { HALT, PUSH, ADD, SUB, MUL, DIV, MOD, POWER }; typedef Value instruc; static instruc code[CODESIZE]; static instruc *here = code; static void gen (instruc opcode) { if (code + CODESIZE <= here) { complain ("Expression too complicated"); return; } *here++ = opcode; } static void gen_push (Value v) { gen (PUSH); gen ((instruc) v); } static Value run() { Value stack[STACKSIZE]; Value *sp = stack + STACKSIZE; instruc *pc = code; #define need(n) \ do { \ if (sp - (n) < stack) \ { \ complain ("Stack overflow"); \ return 0; \ } \ } while (0) for (;;) switch ((unsigned)*pc++) { case HALT: return sp[0]; break; case PUSH: need (1); *--sp = (Value) *pc++; break; case ADD: sp[1] += sp[0]; ++sp; break; case SUB: sp[1] -= sp[0]; ++sp; break; case MUL: sp[1] *= sp[0]; ++sp; break; case DIV: sp[1] /= sp[0]; ++sp; break; case MOD: sp[1] = fmod (sp[1], sp[0]); ++sp; break; case POWER: sp[1] = pow (sp[1], sp[0]); ++sp; break; default: assert (0); } } static const char *p; static int token; static Value token_value; static void next () { char *endptr; switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': token = PUSH; token_value = strtod (p, &endptr); p = endptr; break; case '+': case '-': case '*': case '/': case '%': case '^': case '(': case ')': token = *p++; break; case ' ': case '\t': ++p; next (); break; case '\0': token = '\0'; break; default: complain ("Syntax error: unknown token type"); token = '\0'; break; } } static void parse_expr (int precedence); static void parse_factor () { switch (token) { case PUSH: gen_push (token_value); next (); break; case '-': /* unary minus */ next (); gen_push (0); parse_factor (); gen (SUB); break; case '(': next (); parse_expr (0); if (token != ')') complain ("Syntax error: expected ')'"); next (); break; default: complain ("Syntax error: expected a factor"); next (); } } static void parse_expr (int precedence) { parse_factor (); for (;;) { int l, r, rator; /* left precedence, right precedence, and operator */ switch (token) { case '+': l = 1; r = 2; rator = ADD; break; case '-': l = 1; r = 2; rator = SUB; break; case '*': l = 3; r = 4; rator = MUL; break; case '/': l = 3; r = 4; rator = DIV; break; case '%': l = 3; r = 4; rator = MOD; break; case '^': l = 5; r = 5; rator = POWER; break; default: return; } if (l < precedence) return; next (); parse_expr (r); gen (rator); } } const char *dcalc (Value *result, const char *expression) { *result = 0; complaint = NULL; p = expression; here = code; next (); parse_expr (0); if (*p != '\0') complain ("Syntax error: unexpected token"); if (!complaint) { gen (HALT); *result = run (); } return complaint; }