/* * (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 #include #include #include #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 # 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; }