/* constfold.c expression constant folding Copyright (C) 2004 Bill Currie Author: Bill Currie Date: 2004/01/22 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include "expr.h" #include "options.h" #include "qc-parse.h" #include "qfcc.h" #include "type.h" static __attribute__ ((noreturn)) void internal_error (expr_t *e) { error (e, "internal error"); abort (); } static expr_t * cf_cast_expr (type_t *type, expr_t *e) { e = cast_expr (type, e); // The expression of which this is a sub-expression has already // incremented users, so we don't need cast_expr to do so again, // however, since cast_expr does so unconditionally, we must undo // the increment. if (e && e->type == ex_uexpr && e->e.expr.op == 'C') dec_users (e->e.expr.e1); return e; } static int valid_op (int op, int *valid_ops) { while (*valid_ops && op != *valid_ops) valid_ops++; return *valid_ops == op; } static expr_t * do_op_string (int op, expr_t *e, expr_t *e1, expr_t *e2) { const char *s1, *s2; static dstring_t *temp_str; static int valid[] = {'=', 'b', '+', LT, GT, LE, GE, EQ, NE, 0}; if (!valid_op (op, valid)) return error (e1, "invalid operand for string"); if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_string; } if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; s1 = e1->e.string_val ? e1->e.string_val : ""; s2 = e2->e.string_val ? e2->e.string_val : ""; switch (op) { case '+': if (!temp_str) temp_str = dstring_newstr (); dstring_clearstr (temp_str); dstring_appendstr (temp_str, s1); dstring_appendstr (temp_str, s2); e1->e.string_val = save_string (temp_str->str); break; case LT: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) < 0; break; case GT: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) > 0; break; case LE: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) <= 0; break; case GE: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) >= 0; break; case EQ: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) == 0; break; case NE: e1->type = ex_integer; e1->e.integer_val = strcmp (s1, s2) != 0; break; default: internal_error (e1); } return e1; } static expr_t * convert_to_float (expr_t *e) { if (get_type (e) == &type_float) return e; switch (e->type) { case ex_integer: convert_int (e); return e; case ex_uinteger: convert_uint (e); return e; case ex_short: convert_short (e); return e; case ex_def: case ex_expr: case ex_uexpr: case ex_temp: case ex_block: e = cf_cast_expr (&type_float, e); return e; default: internal_error (e); } } static expr_t * do_op_float (int op, expr_t *e, expr_t *e1, expr_t *e2) { float f1, f2; expr_t *conv; type_t *type = &type_float; static int valid[] = { '=', 'b', '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) return error (e1, "invalid operand for float"); if (op == 'b') { // bind is backwards to assign (why did I do that? :P) if ((type = get_type (e2)) != &type_float) { //FIXME optimize casting a constant e->e.expr.e1 = e1 = cf_cast_expr (type, e1); } else if ((conv = convert_to_float (e1)) != e1) { e->e.expr.e1 = e1 = conv; } } else if (op == '=' || op == PAS) { if ((type = get_type (e1)) != &type_float) { //FIXME optimize casting a constant e->e.expr.e2 = e2 = cf_cast_expr (type, e2); } else if ((conv = convert_to_float (e2)) != e2) { e->e.expr.e2 = e2 = conv; } } else { if ((conv = convert_to_float (e1)) != e1) { e->e.expr.e1 = e1 = conv; } if ((conv = convert_to_float (e2)) != e2) { e->e.expr.e2 = e2 = conv; } } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) type = &type_integer; else type = &type_float; } e->e.expr.type = type; if (op == '*' && is_constant (e1) && e1->e.float_val == 1) return dec_users (e2); if (op == '*' && is_constant (e2) && e2->e.float_val == 1) return dec_users (e1); if (op == '*' && is_constant (e1) && e1->e.float_val == 0) return dec_users (e1); if (op == '*' && is_constant (e2) && e2->e.float_val == 0) return dec_users (e2); if (op == '/' && is_constant (e2) && e2->e.float_val == 1) return dec_users (e1); if (op == '/' && is_constant (e2) && e2->e.float_val == 0) return error (e, "division by zero"); if (op == '/' && is_constant (e1) && e1->e.float_val == 0) return dec_users (e1); if (op == '+' && is_constant (e1) && e1->e.float_val == 0) return dec_users (e2); if (op == '+' && is_constant (e2) && e2->e.float_val == 0) return dec_users (e1); if (op == '-' && is_constant (e2) && e2->e.float_val == 0) return dec_users (e1); if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; f1 = e1->e.float_val; f2 = e2->e.float_val; switch (op) { case '+': e1->e.float_val += f2; break; case '-': e1->e.float_val -= f2; break; case '*': e1->e.float_val *= f2; break; case '/': if (!f2) return error (e1, "divide by zero"); e1->e.float_val /= f2; break; case '&': e1->e.float_val = (int) f1 & (int) f2; break; case '|': e1->e.float_val = (int) f1 | (int) f2; break; case '^': e1->e.float_val = (int) f1 ^ (int) f2; break; case '%': e1->e.float_val = (int) f1 % (int) f2; break; case SHL: e1->e.float_val = (int) f1 << (int) f2; break; case SHR: e1->e.float_val = (int) f1 >> (int) f2; break; case AND: e1->type = ex_integer; e1->e.integer_val = f1 && f2; break; case OR: e1->type = ex_integer; e1->e.integer_val = f1 || f2; break; case LT: e1->type = ex_integer; e1->e.integer_val = f1 < f2; break; case GT: e1->type = ex_integer; e1->e.integer_val = f1 > f2; break; case LE: e1->type = ex_integer; e1->e.integer_val = f1 <= f2; break; case GE: e1->type = ex_integer; e1->e.integer_val = f1 >= f2; break; case EQ: e1->type = ex_integer; e1->e.integer_val = f1 == f2; break; case NE: e1->type = ex_integer; e1->e.integer_val = f1 != f2; break; default: internal_error (e1); } return e1; } static expr_t * do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) { float *v1, *v2; static int valid[] = {'=', 'b', '+', '-', '*', EQ, NE, 0}; expr_t *t; if (get_type (e1) != &type_vector) { if (op != '*') return error (e1, "invalid operand for vector"); t = e1; e->e.expr.e1 = e1 = e2; e2 = t; } if (get_type (e2) != &type_vector) { e->e.expr.e2 = e2 = convert_to_float (e2); if (op != '*' && op != '/') return error (e1, "invalid operand for vector"); } else { if (!valid_op (op, valid)) return error (e1, "invalid operand for vector"); } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } else if (op == '*' && get_type (e2) == &type_vector) { e->e.expr.type = &type_float; } else { e->e.expr.type = &type_vector; } if (op == '*' && is_constant (e1) && e1->type == ex_float && e1->e.float_val == 1) return dec_users (e2); if (op == '*' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 1) return dec_users (e1); if (op == '*' && is_constant (e1) && e1->type == ex_float && e1->e.float_val == 0) return new_vector_expr (vec3_origin); if (op == '*' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 0) return new_vector_expr (vec3_origin); if (op == '/' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 1) return dec_users (e1); if (op == '/' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 0) return error (e, "division by zero"); if (op == '+' && is_constant (e1) && VectorIsZero (e1->e.vector_val)) return dec_users (e2); if (op == '+' && is_constant (e2) && VectorIsZero (e2->e.vector_val)) return dec_users (e1); if (op == '-' && is_constant (e2) && VectorIsZero (e2->e.vector_val)) return dec_users (e1); if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; v1 = e1->e.vector_val; v2 = e2->e.vector_val; switch (op) { case '+': VectorAdd (v1, v2, v1); break; case '-': VectorSubtract (v1, v2, v1); break; case '/': if (!v2[0]) return error (e1, "divide by zero"); VectorScale (v1, 1 / v2[0], v1); break; case '*': if (get_type (e2) == &type_vector) { e1->type = ex_float; e1->e.float_val = DotProduct (v1, v2); } else { VectorScale (v1, v2[0], v1); } break; case EQ: e1->type = ex_integer; e1->e.integer_val = VectorCompare (v1, v2); break; case NE: e1->type = ex_integer; e1->e.integer_val = !VectorCompare (v1, v2); break; default: internal_error (e1); } return e1; } static expr_t * do_op_entity (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type = get_type (e2); if ((op == '.' || op == '&') && type->type == ev_field) { return e; } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; return e; } if ((op != '=' && op != 'b') || type != &type_entity) return error (e1, "invalid operand for entity"); e->e.expr.type = &type_entity; return e; } static expr_t * do_op_field (int op, expr_t *e, expr_t *e1, expr_t *e2) { if (op != '=' && op != 'b') return error (e1, "invalid operand for field"); e->e.expr.type = &type_field; return e; } static expr_t * do_op_func (int op, expr_t *e, expr_t *e1, expr_t *e2) { if (op == 'c') { e->e.expr.type = get_type (e1)->t.func.type; return e; } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; return e; } if (op != '=' && op != 'b') return error (e1, "invalid operand for func"); e->e.expr.type = &type_function; return e; } static expr_t * do_op_pointer (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type; static int valid[] = {'=', 'b', PAS, '&', 'M', '.', EQ, NE, 0}; if (!valid_op (op, valid)) return error (e1, "invalid operand for pointer"); if (op == PAS && (type = get_type (e1)->t.fldptr.type) != get_type (e2)) { // make sure auto-convertions happen expr_t *tmp = new_temp_def_expr (type); expr_t *ass = new_binary_expr ('=', tmp, e2); tmp->file = e1->file; ass->line = e2->line; ass->file = e2->file; ass = fold_constants (ass); if (e->e.expr.e2 == tmp) internal_error (e2); e->e.expr.e2 = ass->e.expr.e2; } if (op == EQ || op == NE) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } if (op != PAS && op != '.' && op != '&' && op != 'M' && extract_type (e1) != extract_type (e2)) return type_mismatch (e1, e2, op); //if ((op == '.' || op == '&') && get_type (e2) == &type_uinteger) { // //FIXME should implement unsigned addressing // e->e.expr.e2 = cf_cast_expr (&type_integer, e2); //} return e; } static expr_t * do_op_quaternion (int op, expr_t *e, expr_t *e1, expr_t *e2) { float *q1, *q2; static int valid[] = {'=', 'b', '+', '-', '*', EQ, NE, 0}; expr_t *t; if (get_type (e1) != &type_quaternion) { if (op != '*') return error (e1, "invalid operand for quaternion"); t = e1; e->e.expr.e1 = e1 = e2; e2 = t; } if (get_type (e2) != &type_quaternion) { e->e.expr.e2 = e2 = convert_to_float (e2); if (op != '*' && op != '/') return error (e1, "invalid operand for quaternion"); } else { if (!valid_op (op, valid)) return error (e1, "invalid operand for quaternion"); } if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_quaternion; } if (op == '*' && is_constant (e1) && e1->type == ex_float && e1->e.float_val == 1) return dec_users (e2); if (op == '*' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 1) return dec_users (e1); if (op == '*' && is_constant (e1) && e1->type == ex_float && e1->e.float_val == 0) return new_quaternion_expr (quat_origin); if (op == '*' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 0) return new_quaternion_expr (quat_origin); if (op == '/' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 1) return dec_users (e1); if (op == '/' && is_constant (e2) && e2->type == ex_float && e2->e.float_val == 0) return error (e, "division by zero"); if (op == '+' && is_constant (e1) && QuatIsZero (e1->e.quaternion_val)) return dec_users (e2); if (op == '+' && is_constant (e2) && QuatIsZero (e2->e.quaternion_val)) return dec_users (e1); if (op == '-' && is_constant (e2) && QuatIsZero (e2->e.quaternion_val)) return dec_users (e1); if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; q1 = e1->e.quaternion_val; q2 = e2->e.quaternion_val; switch (op) { case '+': QuatAdd (q1, q2, q1); break; case '-': QuatSubtract (q1, q2, q1); break; case '/': if (!q2[0]) return error (e1, "divide by zero"); QuatScale (q1, 1 / q2[0], q1); q1[3] /= q2[0]; break; case '*': if (get_type (e2) == &type_quaternion) { QuatMult (q1, q2, q1); } else { QuatScale (q1, q2[0], q1); q1[3] *= q2[0]; } break; case EQ: e1->type = ex_integer; e1->e.integer_val = QuatCompare (q1, q2); break; case NE: e1->type = ex_integer; e1->e.integer_val = !QuatCompare (q1, q2); break; default: internal_error (e1); } return e1; } static expr_t * do_op_integer (int op, expr_t *e, expr_t *e1, expr_t *e2) { int i1, i2; static int valid[] = { '=', 'b', '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) return error (e1, "invalid operand for integer"); if (e1->type == ex_short) convert_short_int (e1); if (e2->type == ex_short) convert_short_int (e2); if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_integer; } if (op == '*' && is_constant (e1) && e1->e.integer_val == 1) return dec_users (e2); if (op == '*' && is_constant (e2) && e2->e.integer_val == 1) return dec_users (e1); if (op == '*' && is_constant (e1) && e1->e.integer_val == 0) return dec_users (e1); if (op == '*' && is_constant (e2) && e2->e.integer_val == 0) return dec_users (e2); if (op == '/' && is_constant (e2) && e2->e.integer_val == 1) return dec_users (e1); if (op == '/' && is_constant (e2) && e2->e.integer_val == 0) return error (e, "division by zero"); if (op == '/' && is_constant (e1) && e1->e.integer_val == 0) return dec_users (e1); if (op == '+' && is_constant (e1) && e1->e.integer_val == 0) return dec_users (e2); if (op == '+' && is_constant (e2) && e2->e.integer_val == 0) return dec_users (e1); if (op == '-' && is_constant (e2) && e2->e.integer_val == 0) return dec_users (e1); if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; i1 = e1->e.integer_val; i2 = e2->e.integer_val; switch (op) { case '+': e1->e.integer_val += i2; break; case '-': e1->e.integer_val -= i2; break; case '*': e1->e.integer_val *= i2; break; case '/': if (options.warnings.integer_divide) warning (e2, "%d / %d == %d", i1, i2, i1 / i2); e1->e.integer_val /= i2; break; case '&': e1->e.integer_val = i1 & i2; break; case '|': e1->e.integer_val = i1 | i2; break; case '^': e1->e.integer_val = i1 ^ i2; break; case '%': e1->e.integer_val = i1 % i2; break; case SHL: e1->e.integer_val = i1 << i2; break; case SHR: e1->e.integer_val = i1 >> i2; break; case AND: e1->e.integer_val = i1 && i2; break; case OR: e1->e.integer_val = i1 || i2; break; case LT: e1->type = ex_integer; e1->e.integer_val = i1 < i2; break; case GT: e1->type = ex_integer; e1->e.integer_val = i1 > i2; break; case LE: e1->type = ex_integer; e1->e.integer_val = i1 <= i2; break; case GE: e1->type = ex_integer; e1->e.integer_val = i1 >= i2; break; case EQ: e1->type = ex_integer; e1->e.integer_val = i1 == i2; break; case NE: e1->type = ex_integer; e1->e.integer_val = i1 != i2; break; default: internal_error (e1); } return e1; } static expr_t * do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) { short i1, i2; static int valid[] = { '=', 'b', '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) return error (e1, "invalid operand for short"); if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) e->e.expr.type = &type_integer; else e->e.expr.type = &type_float; } else { e->e.expr.type = &type_short; } if (op == '=' || op == 'b' || !is_constant (e1) || !is_constant (e2)) return e; i1 = e1->e.short_val; i2 = e2->e.short_val; switch (op) { case '+': e1->e.short_val += i2; break; case '-': e1->e.short_val -= i2; break; case '*': e1->e.short_val *= i2; break; case '/': if (options.warnings.integer_divide) warning (e2, "%d / %d == %d", i1, i2, i1 / i2); e1->e.short_val /= i2; break; case '&': e1->e.short_val = i1 & i2; break; case '|': e1->e.short_val = i1 | i2; break; case '^': e1->e.short_val = i1 ^ i2; break; case '%': e1->e.short_val = i1 % i2; break; case SHL: e1->e.short_val = i1 << i2; break; case SHR: e1->e.short_val = i1 >> i2; break; case AND: e1->e.short_val = i1 && i2; break; case OR: e1->e.short_val = i1 || i2; break; case LT: e1->type = ex_integer; e1->e.integer_val = i1 < i2; break; case GT: e1->type = ex_integer; e1->e.integer_val = i1 > i2; break; case LE: e1->type = ex_integer; e1->e.integer_val = i1 <= i2; break; case GE: e1->type = ex_integer; e1->e.integer_val = i1 >= i2; break; case EQ: e1->type = ex_integer; e1->e.integer_val = i1 == i2; break; case NE: e1->type = ex_integer; e1->e.integer_val = i1 != i2; break; default: internal_error (e1); } return e1; } static expr_t * do_op_struct (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type; if (op != '=' && op != 'b' && op != 'M') return error (e1, "invalid operand for struct"); if ((type = get_type (e1)) != get_type (e2)) return type_mismatch (e1, e2, op); e->e.expr.type = type; return e; } static expr_t * do_op_invalid (int op, expr_t *e, expr_t *e1, expr_t *e2) { dstring_t *t1 = dstring_newstr (); dstring_t *t2 = dstring_newstr (); print_type_str (t1, get_type (e1)); print_type_str (t2, get_type (e2)); //print_expr (e),puts(""); e1 = error (e1, "invalid operands for binary %s: %s %s", get_op_string (op), t1->str, t2->str); dstring_delete (t1); dstring_delete (t2); return e1; } typedef expr_t *(*operation_t) (int op, expr_t *e, expr_t *e1, expr_t *e2); static operation_t op_void[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_invalid, // ev_float do_op_invalid, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_invalid, // ev_quaternion do_op_invalid, // ev_integer do_op_invalid, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_string[ev_type_count] = { do_op_invalid, // ev_void do_op_string, // ev_string do_op_invalid, // ev_float do_op_invalid, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_invalid, // ev_quaternion do_op_invalid, // ev_integer do_op_invalid, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_float[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_float, // ev_float do_op_vector, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_quaternion, // ev_quaternion do_op_float, // ev_integer do_op_float, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_vector[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_vector, // ev_float do_op_vector, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_invalid, // ev_quaternion do_op_vector, // ev_integer do_op_vector, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_entity[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_invalid, // ev_float do_op_invalid, // ev_vector do_op_entity, // ev_entity do_op_entity, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_invalid, // ev_quaternion do_op_invalid, // ev_integer do_op_invalid, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_field[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_invalid, // ev_float do_op_invalid, // ev_vector do_op_invalid, // ev_entity do_op_field, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_invalid, // ev_quaternion do_op_invalid, // ev_integer do_op_invalid, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_func[ev_type_count] = { do_op_func, // ev_void do_op_func, // ev_string do_op_func, // ev_float do_op_func, // ev_vector do_op_func, // ev_entity do_op_func, // ev_field do_op_func, // ev_func do_op_func, // ev_pointer do_op_func, // ev_quaternion do_op_func, // ev_integer do_op_func, // ev_short do_op_func, // ev_invalid }; static operation_t op_pointer[ev_type_count] = { do_op_pointer, // ev_void do_op_pointer, // ev_string do_op_pointer, // ev_float do_op_pointer, // ev_vector do_op_pointer, // ev_entity do_op_pointer, // ev_field do_op_pointer, // ev_func do_op_pointer, // ev_pointer do_op_pointer, // ev_quaternion do_op_pointer, // ev_integer do_op_pointer, // ev_short do_op_pointer, // ev_invalid }; static operation_t op_quaternion[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_quaternion, // ev_float do_op_invalid, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_quaternion, // ev_quaternion do_op_quaternion, // ev_integer do_op_quaternion, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_integer[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_float, // ev_float do_op_vector, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_quaternion, // ev_quaternion do_op_integer, // ev_integer do_op_integer, // ev_short do_op_invalid, // ev_invalid }; static operation_t op_short[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_float, // ev_float do_op_vector, // ev_vector do_op_invalid, // ev_entity do_op_invalid, // ev_field do_op_invalid, // ev_func do_op_invalid, // ev_pointer do_op_quaternion, // ev_quaternion do_op_integer, // ev_integer do_op_short, // ev_short do_op_invalid, // ev_invalid }; static operation_t *do_op[ev_type_count] = { op_void, // ev_void op_string, // ev_string op_float, // ev_float op_vector, // ev_vector op_entity, // ev_entity op_field, // ev_field op_func, // ev_func op_pointer, // ev_pointer op_quaternion, // ev_quaternion op_integer, // ev_integer op_short, // ev_short op_void, // ev_invalid }; expr_t * fold_constants (expr_t *e) { int op; expr_t *e1, *e2; etype_t t1, t2; if (e->type == ex_block) { expr_t *block = new_block_expr (); expr_t *next; block->e.block.result = e->e.block.result; block->line = e->line; block->file = e->file; for (e = e->e.block.head; e; e = next) { next = e->next; e = fold_constants (e); e->next = 0; append_expr (block, e); } return block; } if (e->type == ex_bool) { e->e.bool.e = fold_constants (e->e.bool.e); return e; } if (e->type == ex_uexpr) { if (e->e.expr.e1) e->e.expr.e1 = fold_constants (e->e.expr.e1); return e; } if (e->type != ex_expr) return e; op = e->e.expr.op; e->e.expr.e1 = e1 = fold_constants (e->e.expr.e1); if (e1->type == ex_error) return e1; t1 = extract_type (e1); if (op == 'i' || op == 'n' || op == 'c') return e; e->e.expr.e2 = e2 = fold_constants (e->e.expr.e2); if (e2->type == ex_error) return e2; if (e2->type == ex_label) return e; t2 = extract_type (e2); if (op == 's') return e; if (is_struct (get_type (e1)) && is_struct (get_type (e2))) return do_op_struct (op, e, e1, e2); if (!do_op[t1] || !do_op[t1][t2]) internal_error (e); return do_op[t1][t2] (op, e, e1, e2); }