Stage 1: convert m4_symbol_value** into new object.
[m4.git] / modules / mpeval.c
bloba7027523b0b221113c25bb3e3f38b24be6a9e2a1
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
4 This file is part of GNU M4.
6 GNU M4 is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU M4 is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <config.h>
22 /* Build using only the exported interfaces, unless NDEBUG is set, in
23 which case use private symbols to speed things up as much as possible. */
24 #ifndef NDEBUG
25 # include <m4/m4module.h>
26 #else
27 # include "m4private.h"
28 #endif
30 #if HAVE_GMP_H
31 # include <gmp.h>
32 #endif
34 /* Rename exported symbols for dlpreload()ing. */
35 #define m4_builtin_table mpeval_LTX_m4_builtin_table
36 #define m4_macro_table mpeval_LTX_m4_macro_table
39 /* Maintain each of the builtins implemented in this modules along
40 with their details in a single table for easy maintenance.
42 function macros blind side minargs maxargs */
43 #define builtin_functions \
44 BUILTIN (mpeval, false, true, true, 1, 3 ) \
48 #define numb_set(ans, i) mpq_set (ans, i)
49 #define numb_set_si(ans, i) mpq_set_si (*(ans), (long) i, (unsigned long) 1)
51 #define numb_init(x) mpq_init (x)
52 #define numb_fini(x) mpq_clear (x)
54 #define numb_zerop(x) (mpq_cmp (x, numb_ZERO) == 0)
55 #define numb_positivep(x) (mpq_cmp (x, numb_ZERO) > 0)
56 #define numb_negativep(x) (mpq_cmp (x, numb_ZERO) < 0)
58 #define numb_eq(x, y) numb_set (x, mpq_cmp (x, y) == 0 ? numb_ONE : numb_ZERO)
59 #define numb_ne(x, y) numb_set (x, mpq_cmp (x, y) != 0 ? numb_ONE : numb_ZERO)
60 #define numb_lt(x, y) numb_set (x, mpq_cmp (x, y) < 0 ? numb_ONE : numb_ZERO)
61 #define numb_le(x, y) numb_set (x, mpq_cmp (x, y) <= 0 ? numb_ONE : numb_ZERO)
62 #define numb_gt(x, y) numb_set (x, mpq_cmp (x, y) > 0 ? numb_ONE : numb_ZERO)
63 #define numb_ge(x, y) numb_set (x, mpq_cmp (x, y) >= 0 ? numb_ONE : numb_ZERO)
65 #define numb_lnot(x) numb_set (x, numb_zerop (x) ? numb_ONE : numb_ZERO)
66 #define numb_lior(x, y) numb_set (x, numb_zerop (x) ? y : x)
67 #define numb_land(x, y) numb_set (x, numb_zerop (x) ? numb_ZERO : y)
69 #define reduce1(f1, x) \
70 do \
71 { \
72 number T; \
73 mpq_init (T); \
74 f1 (T, x); \
75 mpq_set (x, T); \
76 mpq_clear (T); \
77 } \
78 while (0)
79 #define reduce2(f2,x,y) \
80 do \
81 { \
82 number T; \
83 mpq_init (T); \
84 f2 (T, (x), (y)); \
85 mpq_set ((x), T); \
86 mpq_clear (T); \
87 } \
88 while (0)
90 #define numb_plus(x, y) reduce2 (mpq_add, x, y)
91 #define numb_minus(x, y) reduce2 (mpq_sub, x, y)
92 #define numb_negate(x) reduce1 (mpq_neg, x)
94 #define numb_times(x, y) reduce2 (mpq_mul, x, y)
95 #define numb_ratio(x, y) reduce2 (mpq_div, x, y)
96 #define numb_invert(x) reduce1 (mpq_inv, x)
98 #define numb_incr(n) numb_plus (n, numb_ONE)
99 #define numb_decr(n) numb_minus (n, numb_ONE)
101 /* Generate prototypes for each builtin handler function. */
102 #define BUILTIN(handler, macros, blind, side, min, max) M4BUILTIN(handler)
103 builtin_functions
104 #undef BUILTIN
107 /* Generate a table for mapping m4 symbol names to handler functions. */
108 m4_builtin m4_builtin_table[] =
110 #define BUILTIN(handler, macros, blind, side, min, max) \
111 { CONC(builtin_, handler), STR(handler), \
112 ((macros ? M4_BUILTIN_GROKS_MACRO : 0) \
113 | (blind ? M4_BUILTIN_BLIND : 0) \
114 | (side ? M4_BUILTIN_SIDE_EFFECT : 0)), \
115 min, max },
116 builtin_functions
117 #undef BUILTIN
119 { NULL, NULL, 0, 0, 0 },
123 /* A table for mapping m4 symbol names to simple expansion text. */
124 m4_macro m4_macro_table[] =
126 /* name text */
127 { "__mpeval__", "" },
128 { NULL, NULL },
132 /* GMP defines mpq_t as a 1-element array of struct. Therefore, `mpq_t'
133 is not compatible with `const mpq_t'. */
134 typedef mpq_t number;
136 static void numb_initialise (void);
137 static void numb_obstack (m4_obstack *obs, const number value,
138 const int radix, int min);
139 static void mpq2mpz (m4 *context, mpz_t z, const number q, const char *noisily);
140 static void mpz2mpq (number q, const mpz_t z);
141 static void numb_divide (number *x, number *y);
142 static void numb_modulo (m4 *context, number *x, number *y);
143 static void numb_and (m4 *context, number *x, number *y);
144 static void numb_ior (m4 *context, number *x, number *y);
145 static void numb_eor (m4 *context, number *x, number *y);
146 static void numb_not (m4 *context, number *x);
147 static void numb_lshift (m4 *context, number *x, number *y);
148 static void numb_rshift (m4 *context, number *x, number *y);
149 #define numb_urshift(c, x, y) numb_rshift (c, x, y)
152 static number numb_ZERO;
153 static number numb_ONE;
155 static int numb_initialised = 0;
157 static void
158 numb_initialise (void)
160 if (numb_initialised)
161 return;
163 numb_init (numb_ZERO);
164 numb_set_si (&numb_ZERO, 0);
166 numb_init (numb_ONE);
167 numb_set_si (&numb_ONE, 1);
169 numb_initialised = 1;
172 static void
173 numb_obstack (m4_obstack *obs, const number value, const int radix,
174 int min)
176 const char *s;
178 mpz_t i;
179 mpz_init (i);
181 mpq_get_num (i, value);
182 s = mpz_get_str (NULL, radix, i);
184 if (*s == '-')
186 obstack_1grow (obs, '-');
187 s++;
189 for (min -= strlen (s); --min >= 0;)
190 obstack_1grow (obs, '0');
192 obstack_grow (obs, s, strlen (s));
194 mpq_get_den (i, value);
195 if (mpz_cmp_si (i, (long) 1) != 0)
197 obstack_1grow (obs, '\\');
198 s = mpz_get_str ((char *) 0, radix, i);
199 obstack_grow (obs, s, strlen (s));
202 mpz_clear (i);
205 #define NOISY ""
206 #define QUIET (char *)0
208 static void
209 mpq2mpz (m4 *context, mpz_t z, const number q, const char *noisily)
211 if (noisily && mpz_cmp_si (mpq_denref (q), (long) 1) != 0)
212 m4_warn (context, 0, NULL, _("loss of precision in eval: %s"), noisily);
214 mpz_div (z, mpq_numref (q), mpq_denref (q));
217 static void
218 mpz2mpq (number q, const mpz_t z)
220 mpq_set_si (q, (long) 0, (unsigned long) 1);
221 mpq_set_num (q, z);
224 static void
225 numb_divide (number * x, number * y)
227 mpq_t qres;
228 mpz_t zres;
230 mpq_init (qres);
231 mpq_div (qres, *x, *y);
233 mpz_init (zres);
234 mpz_div (zres, mpq_numref (qres), mpq_denref (qres));
235 mpq_clear (qres);
237 mpz2mpq (*x, zres);
238 mpz_clear (zres);
241 static void
242 numb_modulo (m4 *context, number * x, number * y)
244 mpz_t xx, yy, res;
246 /* x should be integral */
247 /* y should be integral */
249 mpz_init (xx);
250 mpq2mpz (context, xx, *x, NOISY);
252 mpz_init (yy);
253 mpq2mpz (context, yy, *y, NOISY);
255 mpz_init (res);
256 mpz_mod (res, xx, yy);
258 mpz_clear (xx);
259 mpz_clear (yy);
261 mpz2mpq (*x, res);
262 mpz_clear (res);
265 static void
266 numb_and (m4 *context, number * x, number * y)
268 mpz_t xx, yy, res;
270 /* x should be integral */
271 /* y should be integral */
273 mpz_init (xx);
274 mpq2mpz (context, xx, *x, NOISY);
276 mpz_init (yy);
277 mpq2mpz (context, yy, *y, NOISY);
279 mpz_init (res);
280 mpz_and (res, xx, yy);
282 mpz_clear (xx);
283 mpz_clear (yy);
285 mpz2mpq (*x, res);
286 mpz_clear (res);
289 static void
290 numb_ior (m4 *context, number * x, number * y)
292 mpz_t xx, yy, res;
294 /* x should be integral */
295 /* y should be integral */
297 mpz_init (xx);
298 mpq2mpz (context, xx, *x, NOISY);
300 mpz_init (yy);
301 mpq2mpz (context, yy, *y, NOISY);
303 mpz_init (res);
304 mpz_ior (res, xx, yy);
306 mpz_clear (xx);
307 mpz_clear (yy);
309 mpz2mpq (*x, res);
310 mpz_clear (res);
313 static void
314 numb_eor (m4 *context, number * x, number * y)
316 mpz_t xx, yy, res;
318 /* x should be integral */
319 /* y should be integral */
321 mpz_init (xx);
322 mpq2mpz (context, xx, *x, NOISY);
324 mpz_init (yy);
325 mpq2mpz (context, yy, *y, NOISY);
327 mpz_init (res);
329 #if 0
330 mpz_xor (res, xx, yy);
331 #else /* 0 */
332 /* a^b = (a|b) & !(a&b) */
334 mpz_t and_ab, ior_ab, nand_ab;
336 mpz_init (ior_ab);
337 mpz_ior (ior_ab, xx, yy);
339 mpz_init (and_ab);
340 mpz_and (and_ab, xx, yy);
342 mpz_init (nand_ab);
343 mpz_com (nand_ab, and_ab);
345 mpz_and (res, ior_ab, nand_ab);
347 mpz_clear (and_ab);
348 mpz_clear (ior_ab);
349 mpz_clear (nand_ab);
351 #endif /* 0 */
353 mpz_clear (xx);
354 mpz_clear (yy);
356 mpz2mpq (*x, res);
357 mpz_clear (res);
360 static void
361 numb_not (m4 *context, number * x)
363 mpz_t xx, res;
365 /* x should be integral */
367 mpz_init (xx);
368 mpq2mpz (context, xx, *x, NOISY);
370 mpz_init (res);
371 mpz_com (res, xx);
373 mpz_clear (xx);
375 mpz2mpq (*x, res);
376 mpz_clear (res);
379 static void
380 numb_lshift (m4 *context, number * x, number * y)
382 mpz_t xx, yy, res;
384 /* x should be integral */
385 /* y should be integral */
387 mpz_init (xx);
388 mpq2mpz (context, xx, *x, NOISY);
390 mpz_init (yy);
391 mpq2mpz (context, yy, *y, NOISY);
393 mpz_init (res);
395 /* bug: need to determine if y is too big or negative. */
396 long int exp = mpz_get_si (yy);
397 if (exp >= 0)
399 mpz_mul_2exp (res, xx, (unsigned) exp);
401 else
403 mpz_div_2exp (res, xx, (unsigned) -exp);
407 mpz_clear (xx);
408 mpz_clear (yy);
410 mpz2mpq (*x, res);
411 mpz_clear (res);
414 static void
415 numb_rshift (m4 *context, number * x, number * y)
417 mpz_t xx, yy, res;
419 /* x should be integral */
420 /* y should be integral */
422 mpz_init (xx);
423 mpq2mpz (context, xx, *x, NOISY);
425 mpz_init (yy);
426 mpq2mpz (context, yy, *y, NOISY);
428 mpz_init (res);
430 /* FIXME: bug - need to determine if y is too big or negative */
431 long int exp = mpz_get_si (yy);
432 if (exp >= 0)
434 mpz_div_2exp (res, xx, (unsigned) exp);
436 else
438 mpz_mul_2exp (res, xx, (unsigned) -exp);
442 mpz_clear (xx);
443 mpz_clear (yy);
445 mpz2mpq (*x, res);
446 mpz_clear (res);
449 #define m4_evaluate builtin_mpeval
450 #include "evalparse.c"