1 /* contributed by demoncrat@efnet.#c on march 2001. */ 2 /* edited by megaton. added double precision numbers */ 3 4 #include <assert.h> 5 #include <math.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include "dcalc.h" 9 10 11 static const char *complaint = NULL; 12 13 static void complain (const char *msg) 14 { 15 if (complaint == NULL) 16 complaint = msg; 17 } 18 19 20 enum { 21 HALT, PUSH, ADD, SUB, MUL, DIV, MOD, POWER 22 }; 23 24 typedef Value instruc; 25 26 static instruc code[CODESIZE]; 27 static instruc *here = code; 28 29 static void gen (instruc opcode) 30 { 31 if (code + CODESIZE <= here) 32 { 33 complain ("Expression too complicated"); 34 return; 35 } 36 37 *here++ = opcode; 38 } 39 40 static void gen_push (Value v) 41 { 42 gen (PUSH); 43 gen ((instruc) v); 44 } 45 46 static Value run() 47 { 48 Value stack[STACKSIZE]; 49 Value *sp = stack + STACKSIZE; 50 instruc *pc = code; 51 52 #define need(n) \ 53 do { \ 54 if (sp - (n) < stack) \ 55 { \ 56 complain ("Stack overflow"); \ 57 return 0; \ 58 } \ 59 } while (0) 60 61 for (;;) 62 switch ((unsigned)*pc++) 63 { 64 case HALT: return sp[0]; break; 65 case PUSH: need (1); *--sp = (Value) *pc++; break; 66 case ADD: sp[1] += sp[0]; ++sp; break; 67 case SUB: sp[1] -= sp[0]; ++sp; break; 68 case MUL: sp[1] *= sp[0]; ++sp; break; 69 case DIV: sp[1] /= sp[0]; ++sp; break; 70 case MOD: (long)sp[1] %= (long)sp[0]; ++sp; break; 71 case POWER: sp[1] = pow (sp[1], sp[0]); ++sp; break; 72 default: assert (0); 73 } 74 } 75 76 77 static const char *p; 78 static int token; 79 static Value token_value; 80 81 static void next () 82 { 83 char *endptr; 84 85 switch (*p) 86 { 87 case '0': 88 case '1': 89 case '2': 90 case '3': 91 case '4': 92 case '5': 93 case '6': 94 case '7': 95 case '8': 96 case '9': 97 token = PUSH; 98 token_value = strtod (p, &endptr); 99 p = endptr; 100 break; 101 102 case '+': 103 case '-': 104 case '*': 105 case '/': 106 case '%': 107 case '^': 108 case '(': 109 case ')': 110 token = *p++; 111 break; 112 113 case ' ': 114 case '\t': 115 ++p; 116 next (); 117 break; 118 119 case '\0': 120 token = '\0'; 121 break; 122 123 default: 124 complain ("Syntax error: unknown token type"); 125 token = '\0'; 126 break; 127 } 128 } 129 130 static void parse_expr (int precedence); 131 132 static void parse_factor () 133 { 134 switch (token) 135 { 136 case PUSH: 137 gen_push (token_value); 138 next (); 139 break; 140 141 case '-': /* unary minus */ 142 next (); 143 gen_push (0); 144 parse_factor (); 145 gen (SUB); 146 break; 147 148 case '(': 149 next (); 150 parse_expr (0); 151 if (token != ')') 152 complain ("Syntax error: expected ')'"); 153 next (); 154 break; 155 156 default: 157 complain ("Syntax error: expected a factor"); 158 next (); 159 } 160 } 161 162 static void parse_expr (int precedence) 163 { 164 parse_factor (); 165 for (;;) 166 { 167 int l, r, rator; /* left precedence, right precedence, and operator */ 168 169 switch (token) { 170 case '+': l = 1; r = 2; rator = ADD; break; 171 case '-': l = 1; r = 2; rator = SUB; break; 172 173 case '*': l = 3; r = 4; rator = MUL; break; 174 case '/': l = 3; r = 4; rator = DIV; break; 175 case '%': l = 3; r = 4; rator = MOD; break; 176 177 case '^': l = 5; r = 5; rator = POWER; break; 178 179 default: return; 180 } 181 182 if (l < precedence) 183 return; 184 185 next (); 186 parse_expr (r); 187 gen (rator); 188 } 189 } 190 191 const char *dcalc (Value *result, const char *expression) 192 { 193 *result = 0; 194 195 complaint = NULL; 196 p = expression; 197 here = code; 198 next (); 199 parse_expr (0); 200 if (*p != '\0') 201 complain ("Syntax error: unexpected token"); 202 203 if (!complaint) 204 { 205 gen (HALT); 206 *result = run (); 207 } 208 209 return complaint; 210 } |