| /* |
| * (c) Thomas Pornin 1999, 2000 |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 4. The name of the authors may not be used to endorse or promote |
| * products derived from this software without specific prior written |
| * permission. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
| * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <setjmp.h> |
| #include <limits.h> |
| #include "ucppi.h" |
| #include "tune.h" |
| #include "mem.h" |
| |
| JMP_BUF eval_exception; |
| long eval_line; |
| static int emit_eval_warnings; |
| |
| /* |
| * If you want to hardcode a conversion table, define a static array |
| * of 256 int, and make transient_characters point to it. |
| */ |
| int *transient_characters = 0; |
| |
| #define OCTAL(x) ((x) >= '0' && (x) <= '7') |
| #define DECIM(x) ((x) >= '0' && (x) <= '9') |
| #define HEXAD(x) (((x) >= '0' && (x) <= '9') \ |
| || (x) == 'a' || (x) == 'b' || (x) == 'c' \ |
| || (x) == 'd' || (x) == 'e' || (x) == 'f' \ |
| || (x) == 'A' || (x) == 'B' || (x) == 'C' \ |
| || (x) == 'D' || (x) == 'E' || (x) == 'F') |
| #define OVAL(x) ((int)((x) - '0')) |
| #define DVAL(x) ((int)((x) - '0')) |
| #define HVAL(x) (DECIM(x) ? DVAL(x) \ |
| : (x) == 'a' || (x) == 'A' ? 10 \ |
| : (x) == 'b' || (x) == 'B' ? 11 \ |
| : (x) == 'c' || (x) == 'C' ? 12 \ |
| : (x) == 'd' || (x) == 'D' ? 13 \ |
| : (x) == 'e' || (x) == 'E' ? 14 : 15) |
| |
| #if !defined(NATIVE_UINTMAX) && !defined(SIMUL_UINTMAX) |
| # if __STDC__ && __STDC_VERSION__ >= 199901L |
| # include <stdint.h> |
| # define NATIVE_UINTMAX uintmax_t |
| # define NATIVE_INTMAX intmax_t |
| # else |
| # define NATIVE_UINTMAX unsigned long |
| # define NATIVE_INTMAX long |
| # endif |
| #endif |
| |
| #ifdef NATIVE_UINTMAX |
| |
| typedef NATIVE_UINTMAX ucppulong; |
| typedef NATIVE_INTMAX ucppslong; |
| |
| #define to_ulong(x) ((ucppulong)(x)) |
| #define to_slong(x) ((ucppslong)(x)) |
| |
| #define op_star_u(x, y) ((x) * (y)) |
| |
| static inline ucppulong op_slash_u(ucppulong x, ucppulong y) |
| { |
| if (y == 0) { |
| error(eval_line, "division by 0"); |
| throw(eval_exception); |
| } |
| return x / y; |
| } |
| |
| static inline ucppulong op_pct_u(ucppulong x, ucppulong y) |
| { |
| if (y == 0) { |
| error(eval_line, "division by 0"); |
| throw(eval_exception); |
| } |
| return x % y; |
| } |
| |
| #define op_plus_u(x, y) ((x) + (y)) |
| #define op_minus_u(x, y) ((x) - (y)) |
| #define op_neg_u(x) (-(x)) |
| #define op_lsh_u(x, y) ((x) << (y)) |
| #define op_rsh_u(x, y) ((x) >> (y)) |
| #define op_lt_u(x, y) ((x) < (y)) |
| #define op_leq_u(x, y) ((x) <= (y)) |
| #define op_gt_u(x, y) ((x) > (y)) |
| #define op_geq_u(x, y) ((x) >= (y)) |
| #define op_same_u(x, y) ((x) == (y)) |
| #define op_neq_u(x, y) ((x) != (y)) |
| #define op_and_u(x, y) ((x) & (y)) |
| #define op_circ_u(x, y) ((x) ^ (y)) |
| #define op_or_u(x, y) ((x) | (y)) |
| #define op_lval_u(x) ((x) != 0) |
| #define op_lnot_u(x) (!(x)) |
| #define op_not_u(x) (~(x)) |
| #define op_uplus_u(x) (x) |
| #define op_uminus_u(x) op_neg_u(x) |
| |
| #define op_star_s(x, y) ((x) * (y)) |
| |
| static inline ucppslong op_slash_s(ucppslong x, ucppslong y) |
| { |
| if (y == 0) { |
| error(eval_line, "division by 0"); |
| throw(eval_exception); |
| } |
| return x / y; |
| } |
| |
| static inline ucppslong op_pct_s(ucppslong x, ucppslong y) |
| { |
| if (y == 0) { |
| error(eval_line, "division by 0"); |
| throw(eval_exception); |
| } |
| return x % y; |
| } |
| |
| #define op_plus_s(x, y) ((x) + (y)) |
| #define op_minus_s(x, y) ((x) - (y)) |
| #define op_neg_s(x) (-(x)) |
| #define op_lsh_s(x, y) ((x) << (y)) |
| #define op_rsh_s(x, y) ((x) >> (y)) |
| #define op_lt_s(x, y) ((x) < (y)) |
| #define op_leq_s(x, y) ((x) <= (y)) |
| #define op_gt_s(x, y) ((x) > (y)) |
| #define op_geq_s(x, y) ((x) >= (y)) |
| #define op_same_s(x, y) ((x) == (y)) |
| #define op_neq_s(x, y) ((x) != (y)) |
| #define op_and_s(x, y) ((x) & (y)) |
| #define op_circ_s(x, y) ((x) ^ (y)) |
| #define op_or_s(x, y) ((x) | (y)) |
| #define op_lval_s(x) ((x) != 0) |
| #define op_lnot_s(x) (!(x)) |
| #define op_not_s(x) (~(x)) |
| #define op_uplus_s(x) (x) |
| #define op_uminus_s(x) op_neg_s(x) |
| |
| #define op_promo(x) ((ucppulong)x) |
| #define back_ulong(x) ((unsigned long)x) |
| |
| #else |
| |
| /* |
| * We suppose that the unsigned long is not padded in its memory |
| * representation, and that it has an even binary length. |
| * |
| * We could explore the size of ULONG_MAX with some dichotomic |
| * trick using a recursive #include, but this would be really ugly. |
| */ |
| #define LONGSIZE (CHAR_BIT * sizeof(unsigned long)) |
| #define HALFLONGSIZE (LONGSIZE / 2) |
| #define HUL_MASK ((1UL << HALFLONGSIZE) - 1) |
| #define UL_MS(x) (((x) >> HALFLONGSIZE) & HUL_MASK) |
| #define UL_LS(x) ((x) & HUL_MASK) |
| #define SIGNBIT(x) ((x) & (1UL << (LONGSIZE - 1))) |
| |
| typedef struct { |
| unsigned long msw, lsw; |
| } ucppulong; |
| |
| #define ucppslong ucppulong |
| |
| static ucppulong to_ulong(unsigned long x) |
| { |
| ucppulong y; |
| |
| y.msw = 0; |
| y.lsw = x; |
| return y; |
| } |
| |
| static ucppslong to_slong(long x) |
| { |
| ucppslong y; |
| |
| y.lsw = x; |
| y.msw = x >= 0 ? 0 : (unsigned long)(-1); |
| return y; |
| } |
| |
| static ucppulong op_plus_u(ucppulong x, ucppulong y) |
| { |
| ucppulong z; |
| |
| z.lsw = x.lsw + y.lsw; |
| z.msw = x.msw + y.msw; |
| if (z.lsw < x.lsw) z.msw ++; |
| return z; |
| } |
| |
| static ucppulong op_neg_u(ucppulong x) |
| { |
| ucppulong z; |
| |
| z.lsw = ~x.lsw; |
| z.msw = ~x.msw; |
| if (!(++ z.lsw)) z.msw ++; |
| return z; |
| } |
| |
| #define op_minus_u(x, y) op_plus_u(x, op_neg_u(y)) |
| |
| static ucppulong op_lsh_u(ucppulong x, int s) |
| { |
| ucppulong z; |
| |
| s %= (2 * LONGSIZE); |
| if (s == 0) return x; |
| if (s >= LONGSIZE) { |
| z.lsw = 0; |
| z.msw = x.lsw; |
| return op_lsh_u(z, s - LONGSIZE); |
| } |
| z.lsw = (x.lsw << s); |
| z.msw = (x.msw << s) | (x.lsw >> (LONGSIZE - s)); |
| return z; |
| } |
| |
| static ucppulong op_rsh_u(ucppulong x, int s) |
| { |
| ucppulong z; |
| |
| s %= (2 * LONGSIZE); |
| if (s == 0) return x; |
| if (s >= LONGSIZE) { |
| z.msw = 0; |
| z.lsw = x.msw; |
| return op_rsh_u(z, s - LONGSIZE); |
| } |
| z.lsw = (x.lsw >> s) | (x.msw << (LONGSIZE - s)); |
| z.msw = (x.msw >> s); |
| return z; |
| } |
| |
| static int op_lt_u(ucppulong x, ucppulong y) |
| { |
| if (x.msw < y.msw) return 1; |
| if (x.msw > y.msw) return 0; |
| if (x.lsw < y.lsw) return 1; |
| return 0; |
| } |
| |
| static int op_leq_u(ucppulong x, ucppulong y) |
| { |
| if (x.msw < y.msw) return 1; |
| if (x.msw > y.msw) return 0; |
| if (x.lsw <= y.lsw) return 1; |
| return 0; |
| } |
| |
| #define op_gt_u(x, y) op_lt_u(y, x) |
| #define op_geq_u(x, y) op_leq_u(y, x) |
| |
| static int op_same_u(ucppulong x, ucppulong y) |
| { |
| return x.msw == y.msw && x.lsw == y.lsw; |
| } |
| |
| #define op_neq_u(x, y) (!op_same_u(x, y)) |
| |
| static ucppulong op_and_u(ucppulong x, ucppulong y) |
| { |
| ucppulong z; |
| |
| z.lsw = x.lsw & y.lsw; |
| z.msw = x.msw & y.msw; |
| return z; |
| } |
| |
| static ucppulong op_circ_u(ucppulong x, ucppulong y) |
| { |
| ucppulong z; |
| |
| z.lsw = x.lsw ^ y.lsw; |
| z.msw = x.msw ^ y.msw; |
| return z; |
| } |
| |
| static ucppulong op_or_u(ucppulong x, ucppulong y) |
| { |
| ucppulong z; |
| |
| z.lsw = x.lsw | y.lsw; |
| z.msw = x.msw | y.msw; |
| return z; |
| } |
| |
| #define op_lval_u(x) op_neq_u(x, to_ulong(0)) |
| #define op_lnot_u(x) (!op_lval_u(x)) |
| |
| static ucppulong op_not_u(ucppulong x) |
| { |
| ucppulong z; |
| |
| z.lsw = ~x.lsw; |
| z.msw = ~x.msw; |
| return z; |
| } |
| |
| #define op_uplus_u(x) (x) |
| #define op_uminus_u(x) op_neg_u(x) |
| |
| static ucppulong umul(unsigned long x, unsigned long y) |
| { |
| ucppulong z; |
| unsigned long t00, t01, t10, t11, c = 0, t; |
| |
| t00 = UL_LS(x) * UL_LS(y); |
| t01 = UL_LS(x) * UL_MS(y); |
| t10 = UL_MS(x) * UL_LS(y); |
| t11 = UL_MS(x) * UL_MS(y); |
| t = z.lsw = t00; |
| if (t > (z.lsw += (UL_LS(t01) << HALFLONGSIZE))) c ++; |
| t = z.lsw; |
| if (t > (z.lsw += (UL_LS(t10) << HALFLONGSIZE))) c ++; |
| z.msw = t11 + UL_MS(t10) + UL_MS(t01) + c; |
| return z; |
| } |
| |
| static ucppulong op_star_u(ucppulong x, ucppulong y) |
| { |
| ucppulong z1, z2; |
| |
| z1.lsw = z2.lsw = 0; |
| z1.msw = x.lsw * y.msw; |
| z2.msw = x.msw * y.lsw; |
| return op_plus_u(umul(x.lsw, y.lsw), op_plus_u(z1, z2)); |
| } |
| |
| static void udiv(ucppulong x, ucppulong y, ucppulong *q, ucppulong *r) |
| { |
| int i, j; |
| |
| *q = to_ulong(0); |
| for (i = 2 * LONGSIZE - 1; i >= 0; i --) { |
| if (i >= LONGSIZE && (y.msw & (1UL << (i - LONGSIZE)))) break; |
| if (i < LONGSIZE && (y.lsw & (1UL << i))) break; |
| } |
| if (i < 0) { |
| error(eval_line, "division by 0"); |
| throw(eval_exception); |
| } |
| for (j = 2 * LONGSIZE - 1 - i; j >= 0; j --) { |
| ucppulong a = op_lsh_u(y, j); |
| |
| if (op_leq_u(a, x)) { |
| x = op_minus_u(x, a); |
| if (j >= LONGSIZE) |
| q->msw |= (1UL << (j - LONGSIZE)); |
| else |
| q->lsw |= (1UL << j); |
| } |
| } |
| *r = x; |
| } |
| |
| static ucppulong op_slash_u(ucppulong x, ucppulong y) |
| { |
| ucppulong q, r; |
| |
| udiv(x, y, &q, &r); |
| return q; |
| } |
| |
| static ucppulong op_pct_u(ucppulong x, ucppulong y) |
| { |
| ucppulong q, r; |
| |
| udiv(x, y, &q, &r); |
| return r; |
| } |
| |
| |
| static ucppslong op_star_s(ucppslong x, ucppslong y) |
| { |
| ucppulong a = x, b = y, c; |
| int xn = 0, yn = 0; |
| |
| if (SIGNBIT(x.msw)) { a = op_neg_u(x); xn = 1; } |
| if (SIGNBIT(y.msw)) { b = op_neg_u(y); yn = 1; } |
| c = op_star_u(a, b); |
| /* Turbo C seems to miscompile the ?: operators when operands |
| are ulong/slong structures */ |
| if (xn ^ yn) return op_neg_u(c); |
| return c; |
| } |
| |
| static void sdiv(ucppslong x, ucppslong y, ucppslong *q, ucppslong *r) |
| { |
| ucppulong a = x, b = y, c, d; |
| int xn = 0, yn = 0; |
| |
| if (SIGNBIT(x.msw)) { a = op_neg_u(x); xn = 1; } |
| if (SIGNBIT(y.msw)) { b = op_neg_u(y); yn = 1; } |
| udiv(a, b, &c, &d); |
| if (xn ^ yn) *q = op_neg_u(c); else *q = c; |
| if (xn ^ yn) *r = op_neg_u(d); else *r = d; |
| } |
| |
| static ucppslong op_slash_s(ucppslong x, ucppslong y) |
| { |
| ucppslong q, r; |
| |
| sdiv(x, y, &q, &r); |
| return q; |
| } |
| |
| static ucppslong op_pct_s(ucppslong x, ucppslong y) |
| { |
| ucppslong q, r; |
| |
| sdiv(x, y, &q, &r); |
| return r; |
| } |
| |
| #define op_plus_s(x, y) op_plus_u(x, y) |
| #define op_minus_s(x, y) op_minus_u(x, y) |
| #define op_neg_s(x) op_neg_u(x) |
| #define op_lsh_s(x, y) op_lsh_u(x, y) |
| |
| /* |
| * What happens if x represents a negative value, is implementation |
| * specified. We emit a warning, and extend the sign (which is what |
| * most implementations do). |
| */ |
| static ucppslong op_rsh_s(ucppslong x, int y) |
| { |
| int xn = (SIGNBIT(x.msw) != 0); |
| ucppulong q = op_rsh_u(x, y); |
| |
| if (xn && y > 0) { |
| if (emit_eval_warnings) |
| warning(eval_line, "right shift of a signed negative " |
| "value in #if"); |
| q = op_or_u(q, op_lsh_u(op_not_u(to_ulong(0)), |
| 2 * LONGSIZE - y)); |
| } |
| return q; |
| } |
| |
| static int op_lt_s(ucppslong x, ucppslong y) |
| { |
| int xn = (SIGNBIT(x.msw) != 0); |
| int yn = (SIGNBIT(y.msw) != 0); |
| |
| if (xn && !yn) return 1; |
| if (!xn && yn) return 0; |
| if (xn) return op_lt_u(op_neg_u(y), op_neg_u(x)); |
| return op_lt_u(x, y); |
| } |
| |
| static int op_leq_s(ucppslong x, ucppslong y) |
| { |
| int xn = (SIGNBIT(x.msw) != 0); |
| int yn = (SIGNBIT(y.msw) != 0); |
| |
| if (xn && !yn) return 1; |
| if (!xn && yn) return 0; |
| if (xn) return op_leq_u(op_neg_u(y), op_neg_u(x)); |
| return op_leq_u(x, y); |
| } |
| |
| #define op_gt_s(x, y) op_lt_s(y, x) |
| #define op_geq_s(x, y) op_leq_s(y, x) |
| #define op_same_s(x, y) op_same_u(x, y) |
| #define op_neq_s(x, y) (!op_same_s(x, y)) |
| |
| #define op_and_s(x, y) op_and_u(x, y) |
| #define op_circ_s(x, y) op_circ_u(x, y) |
| #define op_or_s(x, y) op_or_u(x, y) |
| |
| #define op_lval_s(x) op_neq_s(x, to_slong(0)) |
| #define op_lnot_s(x) (!op_lval_s(x)) |
| #define op_not_s(x) op_not_u(x) |
| #define op_uplus_s(x) (x) |
| #define op_uminus_s(x) op_neg_s(x) |
| |
| #define op_promo(x) (x) |
| #define back_ulong(x) ((x).lsw) |
| |
| #endif |
| |
| typedef struct { |
| int sign; |
| union { |
| ucppulong uv; |
| ucppslong sv; |
| } u; |
| } ppval; |
| |
| static int boolval(ppval x) |
| { |
| return x.sign ? op_lval_s(x.u.sv) : op_lval_u(x.u.uv); |
| } |
| |
| #if !defined(WCHAR_SIGNEDNESS) |
| # if CHAR_MIN == 0 |
| # define WCHAR_SIGNEDNESS 0 |
| # else |
| # define WCHAR_SIGNEDNESS 1 |
| # endif |
| #endif |
| |
| /* |
| * Check the suffix, return 1 if it is signed, 0 otherwise. |
| * u, l, ll, ul, lu, ull, llu, and variations with uppercase letters |
| * are legal, no suffix either; other suffixes are not legal. |
| */ |
| static int pp_suffix(char *d, char *refc) |
| { |
| if (!*d) return 1; |
| if (*d == 'u' || *d == 'U') { |
| d ++; |
| if (!*d) return 0; |
| if (*d == 'l' || *d == 'L') { |
| d ++; |
| if (*d == 'l' || *d == 'L') d ++; |
| if (!*d) return 0; |
| goto suffix_error; |
| } |
| goto suffix_error; |
| } |
| if (*d == 'l' || *d == 'L') { |
| d ++; |
| if (*d == 'l' || *d == 'L') d ++; |
| if (!*d) return 1; |
| if (*d == 'u' || *d == 'U') { |
| d ++; |
| if (!*d) return 0; |
| } |
| goto suffix_error; |
| } |
| suffix_error: |
| error(eval_line, "invalid integer constant '%s'", refc); |
| throw(eval_exception); |
| return 666; |
| } |
| |
| static ppval pp_decconst(char *c, int sign, char *refc) |
| { |
| ppval q; |
| |
| q.sign = sign; |
| if (q.sign) { |
| q.u.sv = to_slong(0); |
| for (; DECIM(*c); c ++) { |
| q.u.sv = op_star_s(q.u.sv, to_slong(10)); |
| q.u.sv = op_plus_s(q.u.sv, to_slong(DVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } else { |
| q.u.uv = to_ulong(0); |
| for (; DECIM(*c); c ++) { |
| q.u.uv = op_star_u(q.u.uv, to_ulong(10)); |
| q.u.uv = op_plus_u(q.u.uv, to_ulong(DVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } |
| return q; |
| const_error: |
| error(eval_line, "invalid integer constant '%s'", refc); |
| throw(eval_exception); |
| return q; |
| } |
| |
| static ppval pp_octconst(char *c, int sign, char *refc) |
| { |
| ppval q; |
| |
| q.sign = sign; |
| if (q.sign) { |
| q.u.sv = to_slong(0); |
| for (; OCTAL(*c); c ++) { |
| q.u.sv = op_star_s(q.u.sv, to_slong(8)); |
| q.u.sv = op_plus_s(q.u.sv, to_slong(OVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } else { |
| q.u.uv = to_ulong(0); |
| for (; OCTAL(*c); c ++) { |
| q.u.uv = op_star_u(q.u.uv, to_ulong(8)); |
| q.u.uv = op_plus_u(q.u.uv, to_ulong(OVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } |
| return q; |
| const_error: |
| error(eval_line, "invalid integer constant '%s'", refc); |
| throw(eval_exception); |
| return q; |
| } |
| |
| static ppval pp_hexconst(char *c, int sign, char *refc) |
| { |
| ppval q; |
| |
| q.sign = sign; |
| if (q.sign) { |
| q.u.sv = to_slong(0); |
| for (; HEXAD(*c); c ++) { |
| q.u.sv = op_star_s(q.u.sv, to_slong(16)); |
| q.u.sv = op_plus_s(q.u.sv, to_slong(HVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } else { |
| q.u.uv = to_ulong(0); |
| for (; HEXAD(*c); c ++) { |
| q.u.uv = op_star_u(q.u.uv, to_ulong(16)); |
| q.u.uv = op_plus_u(q.u.uv, to_ulong(HVAL(*c))); |
| } |
| if (HEXAD(*c) || *c == 'x' || *c == 'X') goto const_error; |
| } |
| return q; |
| const_error: |
| error(eval_line, "invalid integer constant '%s'", refc); |
| throw(eval_exception); |
| return q; |
| } |
| |
| static unsigned long pp_char(char *c, char *refc) |
| { |
| unsigned long r = 0; |
| |
| c ++; |
| if (*c == '\\') { |
| int i; |
| |
| c ++; |
| switch (*c) { |
| case 'n': r = '\n'; c ++; break; |
| case 't': r = '\t'; c ++; break; |
| case 'v': r = '\v'; c ++; break; |
| case 'b': r = '\b'; c ++; break; |
| case 'r': r = '\r'; c ++; break; |
| case 'f': r = '\f'; c ++; break; |
| case 'a': r = '\a'; c ++; break; |
| case '\\': r = '\\'; c ++; break; |
| case '\?': r = '\?'; c ++; break; |
| case '\'': r = '\''; c ++; break; |
| case '\"': r = '\"'; c ++; break; |
| case 'u': |
| for (i = 0, c ++; i < 4 && HEXAD(*c); i ++, c ++) { |
| r = (r * 16) + HVAL(*c); |
| } |
| if (i != 4) { |
| error(eval_line, "malformed UCN in %s", refc); |
| throw(eval_exception); |
| } |
| break; |
| case 'U': |
| for (i = 0, c ++; i < 8 && HEXAD(*c); i ++, c ++) { |
| r = (r * 16) + HVAL(*c); |
| } |
| if (i != 8) { |
| error(eval_line, "malformed UCN in %s", refc); |
| throw(eval_exception); |
| } |
| break; |
| case 'x': |
| for (c ++; HEXAD(*c); c ++) r = (r * 16) + HVAL(*c); |
| break; |
| default: |
| if (OCTAL(*c)) { |
| r = OVAL(*(c ++)); |
| if (OCTAL(*c)) r = (r * 8) + OVAL(*(c ++)); |
| if (OCTAL(*c)) r = (r * 8) + OVAL(*(c ++)); |
| } else { |
| error(eval_line, "invalid escape sequence " |
| "'\\%c'", *c); |
| throw(eval_exception); |
| } |
| } |
| } else if (*c == '\'') { |
| error(eval_line, "empty character constant"); |
| throw(eval_exception); |
| } else { |
| r = *((unsigned char *)(c ++)); |
| } |
| |
| if (transient_characters && r < 256) { |
| r = transient_characters[(size_t)r]; |
| } |
| |
| if (*c != '\'' && emit_eval_warnings) { |
| warning(eval_line, "multicharacter constant"); |
| } |
| return r; |
| } |
| |
| static ppval pp_strtoconst(char *refc) |
| { |
| ppval q; |
| char *c = refc, *d; |
| int sign = 1; |
| |
| if (*c == '\'' || *c == 'L') { |
| q.sign = (*c == 'L') ? WCHAR_SIGNEDNESS : 1; |
| if (*c == 'L') c ++; |
| if (q.sign) { |
| q.u.sv = to_slong(pp_char(c, refc)); |
| } else { |
| q.u.uv = to_ulong(pp_char(c, refc)); |
| } |
| return q; |
| } |
| for (d = c; *d; d ++) { |
| if (!HEXAD(*d) && *d != 'x' && *d != 'X') { |
| sign = pp_suffix(d, refc); |
| break; |
| } |
| } |
| if (*c == '0') { |
| c ++; |
| if (*c == 'x' || *c == 'X') { |
| /* hexadecimal constant */ |
| c ++; |
| return pp_hexconst(c, sign, refc); |
| } |
| return pp_octconst(c, sign, refc); |
| } |
| return pp_decconst(c, sign, refc); |
| } |
| |
| /* |
| * Used by #line directives -- anything beyond what can be put in an |
| * unsigned long, is considered absurd. |
| */ |
| unsigned long strtoconst(char *c) |
| { |
| ppval q = pp_strtoconst(c); |
| |
| if (q.sign) q.u.uv = op_promo(q.u.sv); |
| return back_ulong(q.u.uv); |
| } |
| |
| #define OP_UN(x) ((x) == LNOT || (x) == NOT || (x) == UPLUS \ |
| || (x) == UMINUS) |
| |
| static ppval eval_opun(int op, ppval v) |
| { |
| if (op == LNOT) { |
| v.sign = 1; |
| v.u.sv = to_slong(op_lnot_s(v.u.sv)); |
| return v; |
| } |
| if (v.sign) { |
| switch (op) { |
| case NOT: v.u.sv = op_not_s(v.u.sv); break; |
| case UPLUS: v.u.sv = op_uplus_s(v.u.sv); break; |
| case UMINUS: v.u.sv = op_uminus_s(v.u.sv); break; |
| } |
| } else { |
| switch (op) { |
| case NOT: v.u.uv = op_not_u(v.u.uv); break; |
| case UPLUS: v.u.uv = op_uplus_u(v.u.uv); break; |
| case UMINUS: v.u.uv = op_uminus_u(v.u.uv); break; |
| } |
| } |
| return v; |
| } |
| |
| #define OP_BIN(x) ((x) == STAR || (x) == SLASH || (x) == PCT \ |
| || (x) == PLUS || (x) == MINUS || (x) == LSH \ |
| || (x) == RSH || (x) == LT || (x) == LEQ \ |
| || (x) == GT || (x) == GEQ || (x) == SAME \ |
| || (x) == NEQ || (x) == AND || (x) == CIRC \ |
| || (x) == OR || (x) == LAND || (x) == LOR \ |
| || (x) == COMMA) |
| static ppval eval_opbin(int op, ppval v1, ppval v2) |
| { |
| ppval r; |
| |
| switch (op) { |
| case STAR: case SLASH: case PCT: |
| case PLUS: case MINUS: case AND: |
| case CIRC: case OR: |
| /* promote operands, adjust signedness of result */ |
| if (!v1.sign || !v2.sign) { |
| if (v1.sign) { |
| v1.u.uv = op_promo(v1.u.sv); |
| v1.sign = 0; |
| } else if (v2.sign) { |
| v2.u.uv = op_promo(v2.u.sv); |
| v2.sign = 0; |
| } |
| r.sign = 0; |
| } else { |
| r.sign = 1; |
| } |
| break; |
| case LT: case LEQ: case GT: |
| case GEQ: case SAME: case NEQ: |
| /* promote operands */ |
| if (!v1.sign || !v2.sign) { |
| if (v1.sign) { |
| v1.u.uv = op_promo(v1.u.sv); |
| v1.sign = 0; |
| } else if (v2.sign) { |
| v2.u.uv = op_promo(v2.u.sv); |
| v2.sign = 0; |
| } |
| } |
| /* fall through */ |
| case LAND: |
| case LOR: |
| /* result is signed anyway */ |
| r.sign = 1; |
| break; |
| case LSH: |
| case RSH: |
| /* result is as signed as left operand; promote right |
| operand to unsigned (this is not required by the |
| standard) */ |
| r.sign = v1.sign; |
| if (v2.sign) { |
| v2.u.uv = op_promo(v2.u.sv); |
| v2.sign = 0; |
| } |
| break; |
| case COMMA: |
| if (emit_eval_warnings) { |
| warning(eval_line, "ISO C forbids evaluated comma " |
| "operators in #if expressions"); |
| } |
| r.sign = v2.sign; |
| break; |
| #ifdef AUDIT |
| default: ouch("a good operator is a dead operator"); |
| #endif |
| } |
| |
| #define SBINOP(x) if (r.sign) r.u.sv = op_ ## x ## _s(v1.u.sv, v2.u.sv); \ |
| else r.u.uv = op_ ## x ## _u(v1.u.uv, v2.u.uv); |
| |
| #define NSSBINOP(x) if (v1.sign) r.u.sv = to_slong(op_ ## x ## _s(v1.u.sv, \ |
| v2.u.sv)); else r.u.sv = to_slong(op_ ## x ## _u \ |
| (v1.u.uv, v2.u.uv)); |
| |
| #define LBINOP(x) if (v1.sign) r.u.sv = to_slong(op_lval_s(v1.u.sv) x \ |
| op_lval_s(v2.u.sv)); else r.u.sv = \ |
| to_slong(op_lval_u(v1.u.uv) x op_lval_u(v2.u.uv)); |
| |
| #define ABINOP(x) if (r.sign) r.u.sv = op_ ## x ## _s(v1.u.sv, \ |
| back_ulong(v2.u.uv)); else r.u.uv = \ |
| op_ ## x ## _u(v1.u.uv, back_ulong(v2.u.uv)); |
| |
| switch (op) { |
| case STAR: SBINOP(star); break; |
| case SLASH: SBINOP(slash); break; |
| case PCT: SBINOP(pct); break; |
| case PLUS: SBINOP(plus); break; |
| case MINUS: SBINOP(minus); break; |
| case LSH: ABINOP(lsh); break; |
| case RSH: ABINOP(rsh); break; |
| case LT: NSSBINOP(lt); break; |
| case LEQ: NSSBINOP(leq); break; |
| case GT: NSSBINOP(gt); break; |
| case GEQ: NSSBINOP(geq); break; |
| case SAME: NSSBINOP(same); break; |
| case NEQ: NSSBINOP(neq); break; |
| case AND: SBINOP(and); break; |
| case CIRC: SBINOP(circ); break; |
| case OR: SBINOP(or); break; |
| case LAND: LBINOP(&&); break; |
| case LOR: LBINOP(||); break; |
| case COMMA: r = v2; break; |
| } |
| return r; |
| } |
| |
| #define ttOP(x) (OP_UN(x) || OP_BIN(x) || (x) == QUEST || (x) == COLON) |
| |
| static int op_prec(int op) |
| { |
| switch (op) { |
| case LNOT: |
| case NOT: |
| case UPLUS: |
| case UMINUS: |
| return 13; |
| case STAR: |
| case SLASH: |
| case PCT: |
| return 12; |
| case PLUS: |
| case MINUS: |
| return 11; |
| case LSH: |
| case RSH: |
| return 10; |
| case LT: |
| case LEQ: |
| case GT: |
| case GEQ: |
| return 9; |
| case SAME: |
| case NEQ: |
| return 8; |
| case AND: |
| return 7; |
| case CIRC: |
| return 6; |
| case OR: |
| return 5; |
| case LAND: |
| return 4; |
| case LOR: |
| return 3; |
| case QUEST: |
| return 2; |
| case COMMA: |
| return 1; |
| } |
| #ifdef AUDIT |
| ouch("an unknown species should have a higher precedence"); |
| #endif |
| return 666; |
| } |
| |
| /* |
| * Perform the hard work of evaluation. |
| * |
| * This function works because: |
| * -- all unary operators are right to left associative, and with |
| * identical precedence |
| * -- all binary operators are left to right associative |
| * -- there is only one non-unary and non-binary operator: the quest-colon |
| * |
| * If do_eval is 0, the evaluation of operators is not done. This is |
| * for sequence point operators (&&, || and ?:). |
| */ |
| static ppval eval_shrd(struct token_fifo *tf, int minprec, int do_eval) |
| { |
| ppval top; |
| struct token *ct; |
| |
| top.sign = 1; |
| if (tf->art == tf->nt) goto trunc_err; |
| ct = tf->t + (tf->art ++); |
| if (ct->type == LPAR) { |
| top = eval_shrd(tf, 0, do_eval); |
| if (tf->art == tf->nt) goto trunc_err; |
| ct = tf->t + (tf->art ++); |
| if (ct->type != RPAR) { |
| error(eval_line, "a right parenthesis was expected"); |
| throw(eval_exception); |
| } |
| } else if (ct->type == NUMBER || ct->type == CHAR) { |
| top = pp_strtoconst(ct->name); |
| } else if (OP_UN(ct->type)) { |
| top = eval_opun(ct->type, eval_shrd(tf, |
| op_prec(ct->type), do_eval)); |
| goto eval_loop; |
| } else if (ttOP(ct->type)) goto rogue_op_err; |
| else { |
| goto invalid_token_err; |
| } |
| |
| eval_loop: |
| if (tf->art == tf->nt) { |
| return top; |
| } |
| ct = tf->t + (tf->art ++); |
| if (OP_BIN(ct->type)) { |
| int bp = op_prec(ct->type); |
| |
| if (bp > minprec) { |
| ppval tr; |
| |
| if ((ct->type == LOR && boolval(top)) |
| || (ct->type == LAND && !boolval(top))) { |
| tr = eval_shrd(tf, bp, 0); |
| if (do_eval) { |
| top.sign = 1; |
| if (ct->type == LOR) |
| top.u.sv = to_slong(1); |
| if (ct->type == LAND) |
| top.u.sv = to_slong(0); |
| } |
| } else { |
| tr = eval_shrd(tf, bp, do_eval); |
| if (do_eval) |
| top = eval_opbin(ct->type, top, tr); |
| } |
| goto eval_loop; |
| } |
| } else if (ct->type == QUEST) { |
| int bp = op_prec(QUEST); |
| ppval r1, r2; |
| |
| if (bp >= minprec) { |
| int qv = boolval(top); |
| |
| r1 = eval_shrd(tf, bp, qv ? do_eval : 0); |
| if (tf->art == tf->nt) goto trunc_err; |
| ct = tf->t + (tf->art ++); |
| if (ct->type != COLON) { |
| error(eval_line, "a colon was expected"); |
| throw(eval_exception); |
| } |
| r2 = eval_shrd(tf, bp, qv ? 0 : do_eval); |
| if (do_eval) { |
| if (qv) top = r1; else top = r2; |
| } |
| goto eval_loop; |
| } |
| } |
| tf->art --; |
| return top; |
| |
| trunc_err: |
| error(eval_line, "truncated constant integral expression"); |
| throw(eval_exception); |
| rogue_op_err: |
| error(eval_line, "rogue operator '%s' in constant integral " |
| "expression", operators_name[ct->type]); |
| throw(eval_exception); |
| invalid_token_err: |
| error(eval_line, "invalid token in constant integral expression"); |
| throw(eval_exception); |
| } |
| |
| #define UNARY(x) ((x) != NUMBER && (x) != NAME && (x) != CHAR \ |
| && (x) != RPAR) |
| |
| /* |
| * Evaluate the integer expression contained in the given token_fifo. |
| * Evaluation is made by precedence of operators, as described in the |
| * Dragon Book. The unary + and - are distinguished from their binary |
| * counterparts using the Fortran way: a + or a - is considered unary |
| * if it does not follow a constant, an identifier or a right parenthesis. |
| */ |
| unsigned long eval_expr(struct token_fifo *tf, int *ret, int ew) |
| { |
| size_t sart; |
| ppval r; |
| |
| emit_eval_warnings = ew; |
| if (catch(eval_exception)) goto eval_err; |
| /* first, distinguish unary + and - from binary + and - */ |
| for (sart = tf->art; tf->art < tf->nt; tf->art ++) { |
| if (tf->t[tf->art].type == PLUS) { |
| if (sart == tf->art || UNARY(tf->t[tf->art - 1].type)) |
| tf->t[tf->art].type = UPLUS; |
| } else if (tf->t[tf->art].type == MINUS) { |
| if (sart == tf->art || UNARY(tf->t[tf->art - 1].type)) |
| tf->t[tf->art].type = UMINUS; |
| } |
| } |
| tf->art = sart; |
| r = eval_shrd(tf, 0, 1); |
| if (tf->art < tf->nt) { |
| error(eval_line, "trailing garbage in constant integral " |
| "expression"); |
| goto eval_err; |
| } |
| *ret = 0; |
| return boolval(r); |
| eval_err: |
| *ret = 1; |
| return 0; |
| } |