num_exp: RPN calculator
[m6w6/ext-psi] / src / calc.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include <assert.h>
28
29 #include "token.h"
30 #include "calc.h"
31
32 #define PSI_CALC_CAST_SET(in_type, in_val, op, out_val, out_var) \
33 switch (in_type) { \
34 case PSI_T_INT8: (out_val)->out_var op (in_val)->i8; break; \
35 case PSI_T_UINT8: (out_val)->out_var op (in_val)->u8; break; \
36 case PSI_T_INT16: (out_val)->out_var op (in_val)->i16; break; \
37 case PSI_T_UINT16: (out_val)->out_var op (in_val)->u16; break; \
38 case PSI_T_INT32: (out_val)->out_var op (in_val)->i32; break; \
39 case PSI_T_UINT32: (out_val)->out_var op (in_val)->u32; break; \
40 case PSI_T_INT64: (out_val)->out_var op (in_val)->i64; break; \
41 case PSI_T_UINT64: (out_val)->out_var op (in_val)->u64; break; \
42 case PSI_T_FLOAT: (out_val)->out_var op (in_val)->fval; break; \
43 case PSI_T_DOUBLE: (out_val)->out_var op (in_val)->dval; break; \
44 case PSI_T_LONG_DOUBLE: (out_val)->out_var op (in_val)->ldval; break; \
45 default: \
46 assert(0); \
47 }
48
49 #define PSI_CALC_CAST(in_type, in_val, op, out_type, out_val) \
50 switch (out_type) { \
51 case PSI_T_INT8: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, i8) break; \
52 case PSI_T_UINT8: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, u8) break; \
53 case PSI_T_INT16: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, i16) break; \
54 case PSI_T_UINT16: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, u16) break; \
55 case PSI_T_INT32: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, i32) break; \
56 case PSI_T_UINT32: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, u32) break; \
57 case PSI_T_INT64: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, i64) break; \
58 case PSI_T_UINT64: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, u64) break; \
59 case PSI_T_FLOAT: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, fval) break; \
60 case PSI_T_DOUBLE: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, dval) break; \
61 case PSI_T_LONG_DOUBLE: PSI_CALC_CAST_SET(in_type, in_val, op, out_val, ldval) break; \
62 default: \
63 assert(0); \
64 }
65
66 #define PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, out_var) \
67 switch (in_type) { \
68 case PSI_T_INT8: (out_val)->out_var op (in_val)->i8; break; \
69 case PSI_T_UINT8: (out_val)->out_var op (in_val)->u8; break; \
70 case PSI_T_INT16: (out_val)->out_var op (in_val)->i16; break; \
71 case PSI_T_UINT16: (out_val)->out_var op (in_val)->u16; break; \
72 case PSI_T_INT32: (out_val)->out_var op (in_val)->i32; break; \
73 case PSI_T_UINT32: (out_val)->out_var op (in_val)->u32; break; \
74 case PSI_T_INT64: (out_val)->out_var op (in_val)->i64; break; \
75 case PSI_T_UINT64: (out_val)->out_var op (in_val)->u64; break; \
76 default: \
77 assert(0); \
78 }
79 #define PSI_CALC_CAST_INT(in_type, in_val, op, out_type, out_val) \
80 switch (out_type) { \
81 case PSI_T_INT8: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, i8) break; \
82 case PSI_T_UINT8: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, u8) break; \
83 case PSI_T_INT16: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, i16) break; \
84 case PSI_T_UINT16: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, u16) break; \
85 case PSI_T_INT32: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, i32) break; \
86 case PSI_T_UINT32: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, u32) break; \
87 case PSI_T_INT64: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, i64) break; \
88 case PSI_T_UINT64: PSI_CALC_CAST_SET_INT(in_type, in_val, op, out_val, u64) break; \
89 default: \
90 assert(0); \
91 }
92
93 void psi_calc_cast(token_t in_type, impl_val *in_val, token_t out_type, impl_val *out_val)
94 {
95 PSI_CALC_CAST(in_type, in_val, =, out_type, out_val)
96 }
97
98 #if 0
99 #define PSI_CALC_OP(var) do { \
100 const char *fmt = "calc %" PRI##var ", %" PRI##var ": %" PRI##var "\n"; \
101 res->var = PSI_CALC(v1->var, v2->var); \
102 if (!res->var && (v1->var || v2->var)) fprintf(stderr, fmt, v1->var, v2->var, res->var); \
103 } while (0)
104 #define PSI_CALC_OP2(vres, var1, var2) do { \
105 const char *fmt = "calc %" PRI##var1 ", %" PRI##var2 ": %" PRI##vres "\n"; \
106 res->vres = PSI_CALC(v1->var1, v2->var2); \
107 if (!res->vres && (v1->var1 || v2->var2)) fprintf(stderr, fmt, v1->var1, v2->var2, res->vres); \
108 } while(0)
109 #else
110 #define PSI_CALC_OP(var) res->var = PSI_CALC(v1->var, v2->var)
111 #define PSI_CALC_OP2(vres, var1, var2) res->vres = PSI_CALC(v1->var1, v2->var2)
112 #endif
113
114 #ifdef HAVE_LONG_DOUBLE
115 # define PSI_CALC_NO_LD
116 # define PSI_CALC_OP_LD PSI_CALC_OP(ldval)
117 # define PSI_CALC_OP2_LD2(var1) PSI_CALC_OP2(ldval, var1, ldval)
118 # define PSI_CALC_OP2_LD1(var2) PSI_CALC_OP2(ldval, ldval, var2)
119 #else
120 # define PSI_CALC_NO_LD abort()
121 # define PSI_CALC_OP_LD PSI_CALC_NO_LD
122 # define PSI_CALC_OP2_LD2(var) PSI_CALC_NO_LD
123 # define PSI_CALC_OP2_LD1(var) PSI_CALC_NO_LD
124 #endif
125
126 #define PSI_CALC_FN(op) token_t psi_calc_##op(token_t t1, impl_val *v1, token_t t2, impl_val *v2, impl_val *res) \
127 { \
128 if (t1 == t2) { \
129 switch (t1) { \
130 case PSI_T_FLOAT: PSI_CALC_OP(fval); break; \
131 case PSI_T_DOUBLE: PSI_CALC_OP(dval); break; \
132 case PSI_T_LONG_DOUBLE: PSI_CALC_OP_LD; break; \
133 case PSI_T_INT8: PSI_CALC_OP(i8); break; \
134 case PSI_T_UINT8: PSI_CALC_OP(u8); break; \
135 case PSI_T_INT16: PSI_CALC_OP(i16); break; \
136 case PSI_T_UINT16: PSI_CALC_OP(u16); break; \
137 case PSI_T_INT32: PSI_CALC_OP(i32); break; \
138 case PSI_T_UINT32: PSI_CALC_OP(u32); break; \
139 case PSI_T_INT64: PSI_CALC_OP(i64); break; \
140 case PSI_T_UINT64: PSI_CALC_OP(u64); break; \
141 EMPTY_SWITCH_DEFAULT_CASE(); \
142 } \
143 return t1; \
144 } else if (t1 == PSI_T_DOUBLE) { \
145 switch (t2) { \
146 case PSI_T_LONG_DOUBLE: PSI_CALC_OP2_LD2(dval); return t2; \
147 case PSI_T_FLOAT: PSI_CALC_OP2(dval, dval, fval); break; \
148 case PSI_T_INT8: PSI_CALC_OP2(dval, dval, i8); break; \
149 case PSI_T_UINT8: PSI_CALC_OP2(dval, dval, u8); break; \
150 case PSI_T_INT16: PSI_CALC_OP2(dval, dval, i16); break; \
151 case PSI_T_UINT16: PSI_CALC_OP2(dval, dval, u16); break; \
152 case PSI_T_INT32: PSI_CALC_OP2(dval, dval, i32); break; \
153 case PSI_T_UINT32: PSI_CALC_OP2(dval, dval, u32); break; \
154 case PSI_T_INT64: PSI_CALC_OP2(dval, dval, i64); break; \
155 case PSI_T_UINT64: PSI_CALC_OP2(dval, dval, u64); break; \
156 EMPTY_SWITCH_DEFAULT_CASE(); \
157 } \
158 return t1; \
159 } else if (t2 == PSI_T_DOUBLE) { \
160 switch (t1) { \
161 case PSI_T_LONG_DOUBLE: PSI_CALC_OP2_LD1(dval); return t1; \
162 case PSI_T_FLOAT: PSI_CALC_OP2(dval, fval, dval); break; \
163 case PSI_T_INT8: PSI_CALC_OP2(dval, i8, dval); break; \
164 case PSI_T_UINT8: PSI_CALC_OP2(dval, u8, dval); break; \
165 case PSI_T_INT16: PSI_CALC_OP2(dval, i16, dval); break; \
166 case PSI_T_UINT16: PSI_CALC_OP2(dval, u16, dval); break; \
167 case PSI_T_INT32: PSI_CALC_OP2(dval, i32, dval); break; \
168 case PSI_T_UINT32: PSI_CALC_OP2(dval, u32, dval); break; \
169 case PSI_T_INT64: PSI_CALC_OP2(dval, i64, dval); break; \
170 case PSI_T_UINT64: PSI_CALC_OP2(dval, u64, dval); break; \
171 EMPTY_SWITCH_DEFAULT_CASE(); \
172 } \
173 return t2; \
174 } else if (t1 == PSI_T_LONG_DOUBLE) { \
175 PSI_CALC_NO_LD; \
176 switch (t2) { \
177 case PSI_T_DOUBLE: PSI_CALC_OP2_LD1(dval); break; \
178 case PSI_T_FLOAT: PSI_CALC_OP2_LD1(fval); break; \
179 case PSI_T_INT8: PSI_CALC_OP2_LD1(i8); break; \
180 case PSI_T_UINT8: PSI_CALC_OP2_LD1(u8); break; \
181 case PSI_T_INT16: PSI_CALC_OP2_LD1(i16); break; \
182 case PSI_T_UINT16: PSI_CALC_OP2_LD1(u16); break; \
183 case PSI_T_INT32: PSI_CALC_OP2_LD1(i32); break; \
184 case PSI_T_UINT32: PSI_CALC_OP2_LD1(u32); break; \
185 case PSI_T_INT64: PSI_CALC_OP2_LD1(i64); break; \
186 case PSI_T_UINT64: PSI_CALC_OP2_LD1(u64); break; \
187 EMPTY_SWITCH_DEFAULT_CASE(); \
188 } \
189 return t1; \
190 } else if (t2 == PSI_T_LONG_DOUBLE) { \
191 PSI_CALC_NO_LD; \
192 switch (t1) { \
193 case PSI_T_DOUBLE: PSI_CALC_OP2_LD2(dval); break; \
194 case PSI_T_FLOAT: PSI_CALC_OP2_LD2(fval); break; \
195 case PSI_T_INT8: PSI_CALC_OP2_LD2(i8); break; \
196 case PSI_T_UINT8: PSI_CALC_OP2_LD2(u8); break; \
197 case PSI_T_INT16: PSI_CALC_OP2_LD2(i16); break; \
198 case PSI_T_UINT16: PSI_CALC_OP2_LD2(u16); break; \
199 case PSI_T_INT32: PSI_CALC_OP2_LD2(i32); break; \
200 case PSI_T_UINT32: PSI_CALC_OP2_LD2(u32); break; \
201 case PSI_T_INT64: PSI_CALC_OP2_LD2(i64); break; \
202 case PSI_T_UINT64: PSI_CALC_OP2_LD2(u64); break; \
203 EMPTY_SWITCH_DEFAULT_CASE(); \
204 } \
205 return t2; \
206 } else if (t1 == PSI_T_FLOAT) { \
207 switch (t2) { \
208 case PSI_T_LONG_DOUBLE: PSI_CALC_OP2_LD2(fval); return t2; \
209 case PSI_T_DOUBLE: PSI_CALC_OP2(dval, fval, dval); return t2; \
210 case PSI_T_INT8: PSI_CALC_OP2(fval, fval, i8); break; \
211 case PSI_T_UINT8: PSI_CALC_OP2(fval, fval, u8); break; \
212 case PSI_T_INT16: PSI_CALC_OP2(fval, fval, i16); break; \
213 case PSI_T_UINT16: PSI_CALC_OP2(fval, fval, u16); break; \
214 case PSI_T_INT32: PSI_CALC_OP2(fval, fval, i32); break; \
215 case PSI_T_UINT32: PSI_CALC_OP2(fval, fval, u32); break; \
216 case PSI_T_INT64: PSI_CALC_OP2(fval, fval, i64); break; \
217 case PSI_T_UINT64: PSI_CALC_OP2(fval, fval, u64); break; \
218 EMPTY_SWITCH_DEFAULT_CASE(); \
219 } \
220 return t1; \
221 } else if (t2 == PSI_T_FLOAT) { \
222 switch (t1) { \
223 case PSI_T_LONG_DOUBLE: PSI_CALC_OP2_LD1(fval); return t1; \
224 case PSI_T_DOUBLE: PSI_CALC_OP2(dval, dval, fval); return t1; \
225 case PSI_T_INT8: PSI_CALC_OP2(fval, i8, fval); break; \
226 case PSI_T_UINT8: PSI_CALC_OP2(fval, u8, fval); break; \
227 case PSI_T_INT16: PSI_CALC_OP2(fval, i16, fval); break; \
228 case PSI_T_UINT16: PSI_CALC_OP2(fval, u16, fval); break; \
229 case PSI_T_INT32: PSI_CALC_OP2(fval, i32, fval); break; \
230 case PSI_T_UINT32: PSI_CALC_OP2(fval, u32, fval); break; \
231 case PSI_T_INT64: PSI_CALC_OP2(fval, i64, fval); break; \
232 case PSI_T_UINT64: PSI_CALC_OP2(fval, u64, fval); break; \
233 EMPTY_SWITCH_DEFAULT_CASE(); \
234 } \
235 return t2; \
236 } else { \
237 int64_t sval1 = v1->i64, sval2 = v2->i64; \
238 uint64_t uval1 = v1->u64, uval2 = v2->u64; \
239 switch (t1) { \
240 case PSI_T_INT8: sval1 >>= 8; \
241 case PSI_T_INT16: sval1 >>= 8; \
242 case PSI_T_INT32: sval1 >>= 8; \
243 case PSI_T_INT64: \
244 switch (t2) { \
245 case PSI_T_INT8: sval2 >>= 8; \
246 case PSI_T_INT16: sval2 >>= 8; \
247 case PSI_T_INT32: sval2 >>= 8; \
248 case PSI_T_INT64: \
249 res->i64 = PSI_CALC(sval1 , sval2); \
250 return PSI_T_INT64; \
251 case PSI_T_UINT8: uval2 >>= 8; \
252 case PSI_T_UINT16: uval2 >>= 8; \
253 case PSI_T_UINT32: uval2 >>= 8; \
254 case PSI_T_UINT64: \
255 res->i64 = PSI_CALC(sval1, uval2); \
256 return PSI_T_INT64; \
257 } \
258 break; \
259 case PSI_T_UINT8: uval1 >>= 8; \
260 case PSI_T_UINT16: uval1 >>= 8; \
261 case PSI_T_UINT32: uval1 >>= 8; \
262 case PSI_T_UINT64: \
263 switch (t2) { \
264 case PSI_T_INT8: sval2 >>= 8; \
265 case PSI_T_INT16: sval2 >>= 8; \
266 case PSI_T_INT32: sval2 >>= 8; \
267 case PSI_T_INT64: \
268 res->i64 = PSI_CALC(uval1, sval2); \
269 return PSI_T_INT64; \
270 case PSI_T_UINT8: uval2 >>= 8; \
271 case PSI_T_UINT16: uval2 >>= 8; \
272 case PSI_T_UINT32: uval2 >>= 8; \
273 case PSI_T_UINT64: \
274 res->u64 = PSI_CALC(uval1, uval2); \
275 return PSI_T_UINT64; \
276 } \
277 break; \
278 } \
279 } \
280 assert(0); \
281 return 0; \
282 }
283
284 #undef PSI_CALC
285 #define PSI_CALC(var1, var2) (var1) + (var2)
286 PSI_CALC_FN(add)
287 #undef PSI_CALC
288 #define PSI_CALC(var1, var2) (var1) * (var2)
289 PSI_CALC_FN(mul)
290 #undef PSI_CALC
291 #define PSI_CALC(var1, var2) (var1) - (var2)
292 PSI_CALC_FN(sub)
293 #undef PSI_CALC
294 #define PSI_CALC(var1, var2) (var1) / (var2)
295 PSI_CALC_FN(div)
296 #undef PSI_CALC
297
298 token_t psi_calc_mod(token_t t1, impl_val *v1, token_t t2, impl_val *v2, impl_val *res)
299 {
300 impl_val i1, i2;
301
302 PSI_CALC_CAST(t1, v1, =, PSI_T_INT64, &i1);
303 PSI_CALC_CAST(t2, v2, =, PSI_T_INT64, &i2);
304
305 res->i64 = i1.i64 % i2.i64;
306
307 return PSI_T_INT64;
308 }
309
310 #define PSI_CALC_BIT_FN(op) token_t psi_calc_##op(token_t t1, impl_val *v1, token_t t2, impl_val *v2, impl_val *res) \
311 { \
312 impl_val i1, i2; \
313 PSI_CALC_CAST(t1, v1, =, PSI_T_UINT64, &i1); \
314 PSI_CALC_CAST(t2, v2, =, PSI_T_UINT64, &i2); \
315 res->u64 = PSI_CALC(i1.u64, i2.u64); \
316 return PSI_T_UINT64; \
317 }
318
319 #define PSI_CALC(var1, var2) (var1) << (var2)
320 PSI_CALC_BIT_FN(bin_lshift)
321 #undef PSI_CALC
322 #define PSI_CALC(var1, var2) (var1) >> (var2)
323 PSI_CALC_BIT_FN(bin_rshift)
324 #undef PSI_CALC
325 #define PSI_CALC(var1, var2) (var1) & (var2)
326 PSI_CALC_BIT_FN(bin_and)
327 #undef PSI_CALC
328 #define PSI_CALC(var1, var2) (var1) ^ (var2)
329 PSI_CALC_BIT_FN(bin_xor)
330 #undef PSI_CALC
331 #define PSI_CALC(var1, var2) (var1) | (var2)
332 PSI_CALC_BIT_FN(bin_or)
333 #undef PSI_CALC
334
335 token_t psi_calc_not(token_t t1, impl_val *v1, token_t t2, impl_val *v2, impl_val *res)
336 {
337 (void) t2;
338 (void) v2;
339
340 PSI_CALC_CAST(t1, v1, =!, t1, res);
341 return t1;
342 }
343
344 token_t psi_calc_bin_not(token_t t1, impl_val *v1, token_t t2, impl_val *v2, impl_val *res)
345 {
346 impl_val i1;
347
348 (void) t2;
349 (void) v2;
350
351 PSI_CALC_CAST(t1, v1, =, PSI_T_UINT64, &i1);
352 PSI_CALC_CAST_INT(t1, &i1, =~, t1, res);
353 return t1;
354 }