/* 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 "diagnostic.h" #include "expr.h" #include "options.h" #include "qfcc.h" #include "strpool.h" #include "type.h" #include "qc-parse.h" typedef expr_t *(*operation_t) (int op, expr_t *e, expr_t *e1, expr_t *e2); static expr_t * cf_cast_expr (type_t *type, expr_t *e) { e = cast_expr (type, e); 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[] = {'=', '+', 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 == '=' || !is_constant (e1) || !is_constant (e2)) return e; s1 = expr_string (e1); s2 = expr_string (e2); if (!s1) s1 = ""; if (!s2) s2 = ""; switch (op) { case '+': if (!temp_str) temp_str = dstring_newstr (); dstring_clearstr (temp_str); dstring_appendstr (temp_str, s1); dstring_appendstr (temp_str, s2); e = new_string_expr (save_string (temp_str->str)); break; case LT: e = new_integer_expr (strcmp (s1, s2) < 0); break; case GT: e = new_integer_expr (strcmp (s1, s2) > 0); break; case LE: e = new_integer_expr (strcmp (s1, s2) <= 0); break; case GE: e = new_integer_expr (strcmp (s1, s2) >= 0); break; case EQ: e = new_integer_expr (strcmp (s1, s2) == 0); break; case NE: e = new_integer_expr (strcmp (s1, s2)); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } static expr_t * convert_to_float (expr_t *e) { if (get_type (e) == &type_float) return e; switch (e->type) { case ex_value: switch (e->e.value.type) { case ev_integer: convert_int (e); return e; case ev_short: convert_short (e); return e; default: internal_error (e, 0); } break; case ex_symbol: 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, 0); } } 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[] = { '=', '+', '-', '*', '/', '&', '|', '^', '%', 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 == '=' || 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) && expr_float (e1) == 1) return e2; if (op == '*' && is_constant (e2) && expr_float (e2) == 1) return e1; if (op == '*' && is_constant (e1) && expr_float (e1) == 0) return e1; if (op == '*' && is_constant (e2) && expr_float (e2) == 0) return e2; if (op == '/' && is_constant (e2) && expr_float (e2) == 1) return e1; if (op == '/' && is_constant (e2) && expr_float (e2) == 0) return error (e, "division by zero"); if (op == '/' && is_constant (e1) && expr_float (e1) == 0) return e1; if (op == '+' && is_constant (e1) && expr_float (e1) == 0) return e2; if (op == '+' && is_constant (e2) && expr_float (e2) == 0) return e1; if (op == '-' && is_constant (e2) && expr_float (e2) == 0) return e1; if (op == '=' || !is_constant (e1) || !is_constant (e2)) return e; f1 = expr_float (e1); f2 = expr_float (e2); switch (op) { case '+': e = new_float_expr (f1 + f2); break; case '-': e = new_float_expr (f1 - f2); break; case '*': e = new_float_expr (f1 * f2); break; case '/': if (!f2) return error (e1, "divide by zero"); e = new_float_expr (f1 / f2); break; case '&': e = new_float_expr ((int)f1 & (int)f2); break; case '|': e = new_float_expr ((int)f1 | (int)f2); break; case '^': e = new_float_expr ((int)f1 ^ (int)f2); break; case '%': e = new_float_expr ((int)f1 % (int)f2); break; case SHL: e = new_float_expr ((int)f1 << (int)f2); break; case SHR: e = new_float_expr ((int)f1 >> (int)f2); break; case AND: e = new_integer_expr (f1 && f2); break; case OR: e = new_integer_expr (f1 || f2); break; case LT: e = new_integer_expr (f1 < f2); break; case GT: e = new_integer_expr (f1 > f2); break; case LE: e = new_integer_expr (f1 <= f2); break; case GE: e = new_integer_expr (f1 >= f2); break; case EQ: e = new_integer_expr (f1 == f2); break; case NE: e = new_integer_expr (f1 != f2); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } static expr_t * do_op_vector (int op, expr_t *e, expr_t *e1, expr_t *e2) { const float *v1, *v2; vec3_t v; static int valid[] = {'=', '+', '-', '*', 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_float_val (e2) && expr_float (e2) == 1) return e1; if (op == '*' && is_float_val (e2) && expr_float (e2) == 0) return new_vector_expr (vec3_origin); if (op == '/' && is_float_val (e2) && expr_float (e2) == 1) return e1; if (op == '/' && is_float_val (e2) && expr_float (e2) == 0) return error (e, "division by zero"); if (op == '+' && is_constant (e1) && VectorIsZero (expr_vector (e1))) return e2; if (op == '+' && is_constant (e2) && VectorIsZero (expr_vector (e2))) return e1; if (op == '-' && is_constant (e1) && VectorIsZero (expr_vector (e1)) && is_constant (e2)) { vec3_t v; VectorNegate (expr_vector (e2), v); e = new_vector_expr (v); e->file = e2->file; e->line = e2->line; return e; } if (op == '-' && is_constant (e2) && VectorIsZero (expr_vector (e2))) return e1; if (op == '=' || !is_constant (e1) || !is_constant (e2)) return e; v1 = expr_vector (e1); v2 = expr_vector (e2); switch (op) { case '+': VectorAdd (v1, v2, v); e = new_vector_expr (v); break; case '-': VectorSubtract (v1, v2, v); e = new_vector_expr (v); break; case '/': if (!v2[0]) return error (e1, "divide by zero"); VectorScale (v1, 1 / v2[0], v); break; case '*': if (get_type (e2) == &type_vector) { e = new_float_expr (DotProduct (v1, v2)); } else { VectorScale (v1, v2[0], v); e = new_vector_expr (v); } break; case EQ: e = new_integer_expr (VectorCompare (v1, v2)); break; case NE: e = new_integer_expr (!VectorCompare (v1, v2)); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } 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 != '=' || 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 != '=') 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 != '=') 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[] = {'=', 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, 0); 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) { const float *q1, *q2; quat_t q; static int valid[] = {'=', '+', '-', '*', EQ, NE, 0}; expr_t *t; if (get_type (e1) != &type_quaternion) { if (op != '*' && op != '/') return error (e1, "invalid operand for quaternion"); if (op == '*') { 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_float_val (e2) && expr_float (e2) == 1) return e1; if (op == '*' && is_float_val (e2) && expr_float (e2) == 0) return new_quaternion_expr (quat_origin); if (op == '/' && is_float_val (e2) && expr_float (e2) == 1) return e1; if (op == '/' && is_float_val (e2) && expr_float (e2) == 0) return error (e, "division by zero"); if (op == '+' && is_constant (e1) && QuatIsZero (expr_quaternion (e1))) return e2; if (op == '+' && is_constant (e2) && QuatIsZero (expr_quaternion (e2))) return e1; if (op == '-' && is_constant (e2) && QuatIsZero (expr_quaternion (e2))) return e1; if (op == '=' || !is_constant (e1) || !is_constant (e2)) return e; q1 = expr_quaternion (e1); q2 = expr_quaternion (e2); switch (op) { case '+': QuatAdd (q1, q2, q); e = new_quaternion_expr (q); break; case '-': QuatSubtract (q1, q2, q); e = new_quaternion_expr (q); break; case '/': if (is_float_val (e2)) { QuatScale (q1, 1 / expr_float (e2), q); } else { QuatInverse (q2, q); QuatScale (q2, expr_float (e1), q); } e = new_quaternion_expr (q); break; case '*': if (get_type (e2) == &type_quaternion) { QuatMult (q1, q2, q); } else { QuatScale (q1, q2[0], q); } e = new_quaternion_expr (q); break; case EQ: e = new_integer_expr (QuatCompare (q1, q2)); break; case NE: e = new_integer_expr (!QuatCompare (q1, q2)); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } static expr_t * do_op_integer (int op, expr_t *e, expr_t *e1, expr_t *e2) { int i1, i2; static int valid[] = { '=', '+', '-', '*', '/', '&', '|', '^', '%', SHL, SHR, AND, OR, LT, GT, LE, GE, EQ, NE, 0 }; if (!valid_op (op, valid)) return error (e1, "invalid operand for integer"); if (is_short_val (e1)) convert_short_int (e1); if (is_short_val (e2)) 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) && expr_integer (e1) == 1) return e2; if (op == '*' && is_constant (e2) && expr_integer (e2) == 1) return e1; if (op == '*' && is_constant (e1) && expr_integer (e1) == 0) return e1; if (op == '*' && is_constant (e2) && expr_integer (e2) == 0) return e2; if (op == '/' && is_constant (e2) && expr_integer (e2) == 1) return e1; if (op == '/' && is_constant (e2) && expr_integer (e2) == 0) return error (e, "division by zero"); if (op == '/' && is_constant (e1) && expr_integer (e1) == 0) return e1; if (op == '+' && is_constant (e1) && expr_integer (e1) == 0) return e2; if (op == '+' && is_constant (e2) && expr_integer (e2) == 0) return e1; if (op == '-' && is_constant (e2) && expr_integer (e2) == 0) return e1; if (op == '=' || !is_constant (e1) || !is_constant (e2)) return e; i1 = expr_integer (e1); i2 = expr_integer (e2); switch (op) { case '+': e = new_integer_expr (i1 + i2); break; case '-': e = new_integer_expr (i1 - i2); break; case '*': e = new_integer_expr (i1 * i2); break; case '/': if (options.warnings.integer_divide) warning (e2, "%d / %d == %d", i1, i2, i1 / i2); e = new_integer_expr (i1 / i2); break; case '&': e = new_integer_expr (i1 & i2); break; case '|': e = new_integer_expr (i1 | i2); break; case '^': e = new_integer_expr (i1 ^ i2); break; case '%': e = new_integer_expr (i1 % i2); break; case SHL: e = new_integer_expr (i1 << i2); break; case SHR: e = new_integer_expr (i1 >> i2); break; case AND: e = new_integer_expr (i1 && i2); break; case OR: e = new_integer_expr (i1 || i2); break; case LT: e = new_integer_expr (i1 < i2); break; case GT: e = new_integer_expr (i1 > i2); break; case LE: e = new_integer_expr (i1 <= i2); break; case GE: e = new_integer_expr (i1 >= i2); break; case EQ: e = new_integer_expr (i1 == i2); break; case NE: e = new_integer_expr (i1 != i2); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } static expr_t * do_op_short (int op, expr_t *e, expr_t *e1, expr_t *e2) { short i1, i2; static int valid[] = { '=', '+', '-', '*', '/', '&', '|', '^', '%', 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 == '=' || !is_constant (e1) || !is_constant (e2)) return e; i1 = expr_short (e1); i2 = expr_short (e2); switch (op) { case '+': e = new_short_expr (i1 + i2); break; case '-': e = new_short_expr (i1 - i2); break; case '*': e = new_short_expr (i1 * i2); break; case '/': if (options.warnings.integer_divide) warning (e2, "%d / %d == %d", i1, i2, i1 / i2); e = new_short_expr (i1 / i2); break; case '&': e = new_short_expr (i1 & i2); break; case '|': e = new_short_expr (i1 | i2); break; case '^': e = new_short_expr (i1 ^ i2); break; case '%': e = new_short_expr (i1 % i2); break; case SHL: e = new_short_expr (i1 << i2); break; case SHR: e = new_short_expr (i1 >> i2); break; case AND: e = new_short_expr (i1 && i2); break; case OR: e = new_short_expr (i1 || i2); break; case LT: e = new_integer_expr (i1 < i2); break; case GT: e = new_integer_expr (i1 > i2); break; case LE: e = new_integer_expr (i1 <= i2); break; case GE: e = new_integer_expr (i1 >= i2); break; case EQ: e = new_integer_expr (i1 == i2); break; case NE: e = new_integer_expr (i1 != i2); break; default: internal_error (e1, 0); } e->file = e1->file; e->line = e1->line; return e; } static expr_t * do_op_struct (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *type; if (op != '=' && 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_compound (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); if (is_struct (t1) && is_struct (t2)) return do_op_struct (op, e, e1, e2); if (is_scalar (t1) && is_scalar (t2)) { if (is_enum (t1)) { if (t2->type == ev_float) return do_op_float (op, e, e1, e2); return do_op_integer (op, e, e1, e2); } if (is_enum (t2)) { if (t1->type == ev_float) return do_op_float (op, e, e1, e2); return do_op_integer (op, e, e1, e2); } } return error (e1, "invalid operand for compound"); } static operation_t *do_op[ev_type_count]; static expr_t * do_op_invalid (int op, expr_t *e, expr_t *e1, expr_t *e2) { type_t *t1 = get_type (e1); type_t *t2 = get_type (e2); if (is_scalar (t1) && is_scalar (t2)) { // one or both expressions are an enum, and the other is one of // int, float or short. Treat the enum as the other type, or as // the default type if both are enum. etype_t t; if (!is_enum (t1)) t = t1->type; else if (!is_enum (t2)) t = t2->type; else t = type_default->type; return do_op[t][t] (op, e, e1, e2); } else { dstring_t *enc1 = dstring_newstr (); dstring_t *enc2 = dstring_newstr (); print_type_str (enc1, t1); print_type_str (enc2, t2); //print_expr (e); e1 = error (e1, "invalid operands for binary %s: %s %s", get_op_string (op), enc1->str, enc2->str); dstring_delete (enc1); dstring_delete (enc2); return e1; } } 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 op_compound[ev_type_count] = { do_op_invalid, // ev_void do_op_invalid, // ev_string do_op_compound, // 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_compound, // ev_integer do_op_compound, // ev_short do_op_compound, // 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_compound, // 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 (t1 < 0 || t1 >= ev_type_count || t2 < 0 || t2 >= ev_type_count || !do_op[t1] || !do_op[t1][t2]) internal_error (e, "invalid type"); return do_op[t1][t2] (op, e, e1, e2); }