quakeforge/tools/qfcc/source/constfold.c
Bill Currie d5f669af7a Nuke temp reference counting.
The whole reason for this crazy developement branch :)
2011-01-22 11:40:53 +09:00

1067 lines
25 KiB
C

/*
constfold.c
expression constant folding
Copyright (C) 2004 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
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 <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/mathlib.h>
#include "expr.h"
#include "options.h"
#include "qc-parse.h"
#include "qfcc.h"
#include "type.h"
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))) {
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_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);
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, 0);
return do_op[t1][t2] (op, e, e1, e2);
}