quakeforge/tools/qfcc/source/expr_binary.c
Bill Currie 6d5ffa9f8e [build] Move to non-recursive make
There's still some cleanup to do, but everything seems to be working
nicely: `make -j` works, `make distcheck` passes. There is probably
plenty of bitrot in the package directories (RPM, debian), though.

The vc project files have been removed since those versions are way out
of date and quakeforge is pretty much dependent on gcc now anyway.

Most of the old Makefile.am files  are now Makemodule.am.  This should
allow for new Makefile.am files that allow local building (to be added
on an as-needed bases).  The current remaining Makefile.am files are for
standalone sub-projects.a

The installable bins are currently built in the top-level build
directory. This may change if the clutter gets to be too much.

While this does make a noticeable difference in build times, the main
reason for the switch was to take care of the growing dependency issues:
now it's possible to build tools for code generation (eg, using qfcc and
ruamoko programs for code-gen).
2020-06-25 11:35:37 +09:00

1063 lines
26 KiB
C

/*
exprtype.c
Expression type manipulation
Copyright (C) 2013 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2013/06/27
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
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/expr.h"
#include "tools/qfcc/include/options.h"
#include "tools/qfcc/include/type.h"
#include "tools/qfcc/source/qc-parse.h"
typedef struct {
int op;
type_t *type;
type_t *a_cast;
type_t *b_cast;
expr_t *(*process)(int op, expr_t *e1, expr_t *e2);
} expr_type_t;
static expr_t *pointer_arithmetic (int op, expr_t *e1, expr_t *e2);
static expr_t *pointer_compare (int op, expr_t *e1, expr_t *e2);
static expr_t *inverse_multiply (int op, expr_t *e1, expr_t *e2);
static expr_t *double_compare (int op, expr_t *e1, expr_t *e2);
static expr_type_t string_string[] = {
{'+', &type_string},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t float_float[] = {
{'+', &type_float},
{'-', &type_float},
{'*', &type_float},
{'/', &type_float},
{'&', &type_float},
{'|', &type_float},
{'^', &type_float},
{'%', &type_float},
{MOD, &type_float},
{SHL, &type_float},
{SHR, &type_float},
{AND, &type_integer},
{OR, &type_integer},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t float_vector[] = {
{'*', &type_vector},
{0, 0}
};
static expr_type_t float_quat[] = {
{'*', &type_quaternion},
{0, 0}
};
static expr_type_t float_integer[] = {
{'+', &type_float, 0, &type_float},
{'-', &type_float, 0, &type_float},
{'*', &type_float, 0, &type_float},
{'/', &type_float, 0, &type_float},
{'&', &type_float, 0, &type_float},
{'|', &type_float, 0, &type_float},
{'^', &type_float, 0, &type_float},
{'%', &type_float, 0, &type_float},
{MOD, &type_float, 0, &type_float},
{SHL, &type_float, 0, &type_float},
{SHR, &type_float, 0, &type_float},
{EQ, &type_integer, 0, &type_float},
{NE, &type_integer, 0, &type_float},
{LE, &type_integer, 0, &type_float},
{GE, &type_integer, 0, &type_float},
{LT, &type_integer, 0, &type_float},
{GT, &type_integer, 0, &type_float},
{0, 0}
};
#define float_uinteger float_integer
#define float_short float_integer
static expr_type_t float_double[] = {
{'+', &type_double, &type_double, 0},
{'-', &type_double, &type_double, 0},
{'*', &type_double, &type_double, 0},
{'/', &type_double, &type_double, 0},
{'%', &type_double, &type_double, 0},
{MOD, &type_double, &type_double, 0},
{EQ, 0, 0, 0, double_compare},
{NE, 0, 0, 0, double_compare},
{LE, 0, 0, 0, double_compare},
{GE, 0, 0, 0, double_compare},
{LT, 0, 0, 0, double_compare},
{GT, 0, 0, 0, double_compare},
{0, 0}
};
static expr_type_t vector_float[] = {
{'*', &type_vector},
{'/', 0, 0, 0, inverse_multiply},
{0, 0}
};
static expr_type_t vector_vector[] = {
{'+', &type_vector},
{'-', &type_vector},
{'*', &type_float},
{EQ, &type_integer},
{NE, &type_integer},
{0, 0}
};
#define vector_integer vector_float
#define vector_uinteger vector_float
#define vector_short vector_float
static expr_type_t vector_double[] = {
{'*', &type_vector},
{'/', 0, 0, 0, inverse_multiply},
{0, 0}
};
static expr_type_t entity_entity[] = {
{EQ, &type_integer},
{NE, &type_integer},
{0, 0}
};
static expr_type_t field_field[] = {
{EQ, &type_integer},
{NE, &type_integer},
{0, 0}
};
static expr_type_t func_func[] = {
{EQ, &type_integer},
{NE, &type_integer},
{0, 0}
};
static expr_type_t pointer_pointer[] = {
{'-', 0, 0, 0, pointer_arithmetic},
{EQ, 0, 0, 0, pointer_compare},
{NE, 0, 0, 0, pointer_compare},
{LE, 0, 0, 0, pointer_compare},
{GE, 0, 0, 0, pointer_compare},
{LT, 0, 0, 0, pointer_compare},
{GT, 0, 0, 0, pointer_compare},
{0, 0}
};
static expr_type_t pointer_integer[] = {
{'+', 0, 0, 0, pointer_arithmetic},
{'-', 0, 0, 0, pointer_arithmetic},
{0, 0}
};
#define pointer_uinteger pointer_integer
#define pointer_short pointer_integer
static expr_type_t quat_float[] = {
{'*', &type_quaternion},
{'/', 0, 0, 0, inverse_multiply},
{0, 0}
};
static expr_type_t quat_vector[] = {
{'*', &type_vector},
{0, 0}
};
static expr_type_t quat_quat[] = {
{'+', &type_quaternion},
{'-', &type_quaternion},
{'*', &type_quaternion},
{EQ, &type_integer},
{NE, &type_integer},
{0, 0}
};
static expr_type_t quat_integer[] = {
{'*', &type_quaternion, 0, &type_float},
{'/', 0, 0, 0, inverse_multiply},
{0, 0}
};
#define quat_uinteger quat_integer
#define quat_short quat_integer
static expr_type_t quat_double[] = {
{'*', &type_quaternion},
{'/', 0, 0, 0, inverse_multiply},
{0, 0}
};
static expr_type_t integer_float[] = {
{'+', &type_float, &type_float, 0},
{'-', &type_float, &type_float, 0},
{'*', &type_float, &type_float, 0},
{'/', &type_float, &type_float, 0},
{'&', &type_float, &type_float, 0},
{'|', &type_float, &type_float, 0},
{'^', &type_float, &type_float, 0},
{'%', &type_float, &type_float, 0},
{MOD, &type_float, &type_float, 0},
{SHL, &type_integer, 0, &type_integer}, //FIXME?
{SHR, &type_integer, 0, &type_integer}, //FIXME?
{EQ, &type_integer, &type_float, 0},
{NE, &type_integer, &type_float, 0},
{LE, &type_integer, &type_float, 0},
{GE, &type_integer, &type_float, 0},
{LT, &type_integer, &type_float, 0},
{GT, &type_integer, &type_float, 0},
{0, 0}
};
static expr_type_t integer_vector[] = {
{'*', &type_vector, &type_float, 0},
{0, 0}
};
static expr_type_t integer_pointer[] = {
{'+', 0, 0, 0, pointer_arithmetic},
{0, 0}
};
static expr_type_t integer_quat[] = {
{'*', &type_quaternion, &type_float, 0},
{0, 0}
};
static expr_type_t integer_integer[] = {
{'+', &type_integer},
{'-', &type_integer},
{'*', &type_integer},
{'/', &type_integer},
{'&', &type_integer},
{'|', &type_integer},
{'^', &type_integer},
{'%', &type_integer},
{MOD, &type_integer},
{SHL, &type_integer},
{SHR, &type_integer},
{AND, &type_integer},
{OR, &type_integer},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t integer_uinteger[] = {
{'+', &type_integer},
{'-', &type_integer},
{'*', &type_integer},
{'/', &type_integer},
{'&', &type_integer},
{'|', &type_integer},
{'^', &type_integer},
{'%', &type_integer},
{MOD, &type_integer},
{SHL, &type_integer},
{SHR, &type_integer},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t integer_short[] = {
{'+', &type_integer, 0, &type_integer},
{'-', &type_integer, 0, &type_integer},
{'*', &type_integer, 0, &type_integer},
{'/', &type_integer, 0, &type_integer},
{'&', &type_integer, 0, &type_integer},
{'|', &type_integer, 0, &type_integer},
{'^', &type_integer, 0, &type_integer},
{'%', &type_integer, 0, &type_integer},
{MOD, &type_integer, 0, &type_integer},
{SHL, &type_integer, 0, &type_integer},
{SHR, &type_integer, 0, &type_integer},
{EQ, &type_integer, 0, &type_integer},
{NE, &type_integer, 0, &type_integer},
{LE, &type_integer, 0, &type_integer},
{GE, &type_integer, 0, &type_integer},
{LT, &type_integer, 0, &type_integer},
{GT, &type_integer, 0, &type_integer},
{0, 0}
};
static expr_type_t integer_double[] = {
{'+', &type_double, &type_double, 0},
{'-', &type_double, &type_double, 0},
{'*', &type_double, &type_double, 0},
{'/', &type_double, &type_double, 0},
{'%', &type_double, &type_double, 0},
{MOD, &type_double, &type_double, 0},
{EQ, &type_integer, &type_double, 0},
{NE, &type_integer, &type_double, 0},
{LE, &type_integer, &type_double, 0},
{GE, &type_integer, &type_double, 0},
{LT, &type_integer, &type_double, 0},
{GT, &type_integer, &type_double, 0},
{0, 0}
};
#define uinteger_float integer_float
#define uinteger_vector integer_vector
#define uinteger_pointer integer_pointer
#define uinteger_quat integer_quat
static expr_type_t uinteger_integer[] = {
{'+', &type_integer},
{'-', &type_integer},
{'*', &type_integer},
{'/', &type_integer},
{'&', &type_integer},
{'|', &type_integer},
{'^', &type_integer},
{'%', &type_integer},
{MOD, &type_integer},
{SHL, &type_uinteger},
{SHR, &type_uinteger},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t uinteger_uinteger[] = {
{'+', &type_uinteger},
{'-', &type_uinteger},
{'*', &type_uinteger},
{'/', &type_uinteger},
{'&', &type_uinteger},
{'|', &type_uinteger},
{'^', &type_uinteger},
{'%', &type_uinteger},
{MOD, &type_uinteger},
{SHL, &type_uinteger},
{SHR, &type_uinteger},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
#define uinteger_short uinteger_integer
#define uinteger_double integer_double
#define short_float integer_float
#define short_vector integer_vector
#define short_pointer integer_pointer
#define short_quat integer_quat
static expr_type_t short_integer[] = {
{'+', &type_integer, &type_integer, 0},
{'-', &type_integer, &type_integer, 0},
{'*', &type_integer, &type_integer, 0},
{'/', &type_integer, &type_integer, 0},
{'&', &type_integer, &type_integer, 0},
{'|', &type_integer, &type_integer, 0},
{'^', &type_integer, &type_integer, 0},
{'%', &type_integer, &type_integer, 0},
{MOD, &type_integer, &type_integer, 0},
{SHL, &type_short},
{SHR, &type_short},
{EQ, &type_integer, &type_integer, 0},
{NE, &type_integer, &type_integer, 0},
{LE, &type_integer, &type_integer, 0},
{GE, &type_integer, &type_integer, 0},
{LT, &type_integer, &type_integer, 0},
{GT, &type_integer, &type_integer, 0},
{0, 0}
};
static expr_type_t short_uinteger[] = {
{'+', &type_uinteger, &type_uinteger, 0},
{'-', &type_uinteger, &type_uinteger, 0},
{'*', &type_uinteger, &type_uinteger, 0},
{'/', &type_uinteger, &type_uinteger, 0},
{'&', &type_uinteger, &type_uinteger, 0},
{'|', &type_uinteger, &type_uinteger, 0},
{'^', &type_uinteger, &type_uinteger, 0},
{'%', &type_uinteger, &type_uinteger, 0},
{MOD, &type_uinteger, &type_uinteger, 0},
{SHL, &type_short},
{SHR, &type_short},
{EQ, &type_integer, &type_uinteger, 0},
{NE, &type_integer, &type_uinteger, 0},
{LE, &type_integer, &type_uinteger, 0},
{GE, &type_integer, &type_uinteger, 0},
{LT, &type_integer, &type_uinteger, 0},
{GT, &type_integer, &type_uinteger, 0},
{0, 0}
};
static expr_type_t short_short[] = {
{'+', &type_short},
{'-', &type_short},
{'*', &type_short},
{'/', &type_short},
{'&', &type_short},
{'|', &type_short},
{'^', &type_short},
{'%', &type_short},
{MOD, &type_short},
{SHL, &type_short},
{SHR, &type_short},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
#define short_double integer_double
static expr_type_t double_float[] = {
{'+', &type_double, 0, &type_double},
{'-', &type_double, 0, &type_double},
{'*', &type_double, 0, &type_double},
{'/', &type_double, 0, &type_double},
{'%', &type_double, 0, &type_double},
{MOD, &type_double, 0, &type_double},
{EQ, 0, 0, 0, double_compare},
{NE, 0, 0, 0, double_compare},
{LE, 0, 0, 0, double_compare},
{GE, 0, 0, 0, double_compare},
{LT, 0, 0, 0, double_compare},
{GT, 0, 0, 0, double_compare},
{0, 0}
};
static expr_type_t double_vector[] = {
{'*', &type_vector},
{0, 0}
};
static expr_type_t double_quat[] = {
{'*', &type_quaternion},
{0, 0}
};
static expr_type_t double_integer[] = {
{'+', &type_double, 0, &type_double},
{'-', &type_double, 0, &type_double},
{'*', &type_double, 0, &type_double},
{'/', &type_double, 0, &type_double},
{'%', &type_double, 0, &type_double},
{MOD, &type_double, 0, &type_double},
{EQ, 0, 0, 0, double_compare},
{NE, 0, 0, 0, double_compare},
{LE, 0, 0, 0, double_compare},
{GE, 0, 0, 0, double_compare},
{LT, 0, 0, 0, double_compare},
{GT, 0, 0, 0, double_compare},
{0, 0}
};
#define double_uinteger double_integer
#define double_short double_integer
static expr_type_t double_double[] = {
{'+', &type_double},
{'-', &type_double},
{'*', &type_double},
{'/', &type_double},
{'%', &type_double},
{MOD, &type_double},
{EQ, &type_integer},
{NE, &type_integer},
{LE, &type_integer},
{GE, &type_integer},
{LT, &type_integer},
{GT, &type_integer},
{0, 0}
};
static expr_type_t *string_x[ev_type_count] = {
0, // ev_void
string_string,
0, // ev_float
0, // ev_vector
0, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
0, // ev_quat
0, // ev_integer
0, // ev_uinteger
0, // ev_short
0, // ev_double
};
static expr_type_t *float_x[ev_type_count] = {
0, // ev_void
0, // ev_string
float_float,
float_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
float_quat,
float_integer,
float_uinteger,
float_short,
float_double,
};
static expr_type_t *vector_x[ev_type_count] = {
0, // ev_void
0, // ev_string
vector_float,
vector_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
0, // ev_quaternion
vector_integer,
vector_uinteger,
vector_short,
vector_double,
};
static expr_type_t *entity_x[ev_type_count] = {
0, // ev_void
0, // ev_string
0, // ev_float
0, // ev_vector
entity_entity, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
0, // ev_quaternion
0, // ev_integer
0, // ev_uinteger
0, // ev_short
0, // ev_double
};
static expr_type_t *field_x[ev_type_count] = {
0, // ev_void
0, // ev_string
0, // ev_float
0, // ev_vector
0, // ev_entity
field_field, // ev_field
0, // ev_func
0, // ev_pointer
0, // ev_quaternion
0, // ev_integer
0, // ev_uinteger
0, // ev_short
0, // ev_double
};
static expr_type_t *func_x[ev_type_count] = {
0, // ev_void
0, // ev_string
0, // ev_float
0, // ev_vector
0, // ev_entity
0, // ev_field
func_func, // ev_func
0, // ev_pointer
0, // ev_quaternion
0, // ev_integer
0, // ev_uinteger
0, // ev_short
0, // ev_double
};
static expr_type_t *pointer_x[ev_type_count] = {
0, // ev_void
0, // ev_string
0, // ev_float
0, // ev_vector
0, // ev_entity
0, // ev_field
0, // ev_func
pointer_pointer,
0, // ev_quat
pointer_integer,
pointer_uinteger,
pointer_short,
0, // ev_double
};
static expr_type_t *quat_x[ev_type_count] = {
0, // ev_void
0, // ev_string
quat_float,
quat_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
quat_quat,
quat_integer,
quat_uinteger,
quat_short,
quat_double,
};
static expr_type_t *integer_x[ev_type_count] = {
0, // ev_void
0, // ev_string
integer_float,
integer_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
integer_pointer,
integer_quat,
integer_integer,
integer_uinteger,
integer_short,
integer_double,
};
static expr_type_t *uinteger_x[ev_type_count] = {
0, // ev_void
0, // ev_string
uinteger_float,
uinteger_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
uinteger_pointer,
uinteger_quat,
uinteger_integer,
uinteger_uinteger,
uinteger_short,
uinteger_double,
};
static expr_type_t *short_x[ev_type_count] = {
0, // ev_void
0, // ev_string
short_float,
short_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
short_pointer,
short_quat,
short_integer,
short_uinteger,
short_short,
short_double,
};
static expr_type_t *double_x[ev_type_count] = {
0, // ev_void
0, // ev_string
double_float,
double_vector,
0, // ev_entity
0, // ev_field
0, // ev_func
0, // ev_pointer
double_quat,
double_integer,
double_uinteger,
double_short,
double_double,
};
static expr_type_t **binary_expr_types[ev_type_count] = {
0, // ev_void
string_x,
float_x,
vector_x,
entity_x,
field_x,
func_x,
pointer_x,
quat_x,
integer_x,
uinteger_x,
short_x,
double_x
};
static expr_t *
pointer_arithmetic (int op, expr_t *e1, expr_t *e2)
{
expr_t *e;
type_t *t1 = get_type (e1);
type_t *t2 = get_type (e2);
expr_t *ptr;
expr_t *offset;
expr_t *psize;
type_t *ptype;
if (!is_pointer (t1) && !is_pointer (t2)) {
internal_error (e1, "pointer arithmetic on non-pointers");
}
if (is_pointer (t1) && is_pointer (t2)) {
if (op != '-') {
return error (e2, "invalid pointer operation");
}
if (t1 != t2) {
return error (e2, "cannot use %c on pointers of different types",
op);
}
e1 = cast_expr (&type_integer, e1);
e2 = cast_expr (&type_integer, e2);
psize = new_integer_expr (type_size (t1->t.fldptr.type));
return binary_expr ('/', binary_expr ('-', e1, e2), psize);
} else if (is_pointer (t1)) {
offset = cast_expr (&type_integer, e2);
ptr = cast_expr (&type_integer, e1);
ptype = t1;
} else if (is_pointer (t2)) {
offset = cast_expr (&type_integer, e1);
ptr = cast_expr (&type_integer, e2);
ptype = t2;
}
psize = new_integer_expr (type_size (ptype->t.fldptr.type));
e = binary_expr (op, ptr, binary_expr ('*', offset, psize));
return cast_expr (ptype, e);
}
static expr_t *
pointer_compare (int op, expr_t *e1, expr_t *e2)
{
type_t *t1 = get_type (e1);
type_t *t2 = get_type (e2);
expr_t *e;
if (!type_assignable (t1, t2)) {
return error (e2, "cannot use %s on pointers of different types",
get_op_string (op));
}
e = new_binary_expr (op, e1, e2);
e->e.expr.type = &type_integer;
return e;
}
static expr_t *
inverse_multiply (int op, expr_t *e1, expr_t *e2)
{
// There is no vector/float or quaternion/float instruction and adding
// one would mean the engine would have to do 1/f every time
expr_t *one = new_float_expr (1);
return binary_expr ('*', e1, binary_expr ('/', one, e2));
}
static expr_t *
double_compare (int op, expr_t *e1, expr_t *e2)
{
type_t *t1 = get_type (e1);
type_t *t2 = get_type (e2);
expr_t *e;
if (is_constant (e1) && e1->implicit && is_double (t1) && is_float (t2)) {
t1 = &type_float;
convert_double (e1);
}
if (is_float (t1) && is_constant (e2) && e2->implicit && is_double (t2)) {
t2 = &type_float;
convert_double (e2);
}
if (is_double (t1)) {
if (is_float (t2)) {
warning (e2, "comparison between double and float");
} else if (!is_constant (e2)) {
warning (e2, "comparison between double and integer");
}
e2 = cast_expr (&type_double, e2);
} else if (is_double (t2)) {
if (is_float (t1)) {
warning (e1, "comparison between float and double");
} else if (!is_constant (e1)) {
warning (e1, "comparison between integer and double");
}
e1 = cast_expr (&type_double, e1);
}
e = new_binary_expr (op, e1, e2);
e->e.expr.type = &type_integer;
return e;
}
static expr_t *
invalid_binary_expr (int op, expr_t *e1, expr_t *e2)
{
etype_t t1, t2;
t1 = extract_type (e1);
t2 = extract_type (e2);
return error (e1, "invalid binary expression: %s %s %s",
pr_type_name[t1], get_op_string (op), pr_type_name[t2]);
}
static expr_t *
reimplement_binary_expr (int op, expr_t *e1, expr_t *e2)
{
expr_t *e;
if (options.code.progsversion == PROG_ID_VERSION) {
switch (op) {
case '%':
{
expr_t *tmp1, *tmp2;
e = new_block_expr ();
tmp1 = new_temp_def_expr (&type_float);
tmp2 = new_temp_def_expr (&type_float);
append_expr (e, assign_expr (tmp1, binary_expr ('/', e1, e2)));
append_expr (e, assign_expr (tmp2, binary_expr ('&', tmp1, tmp1)));
e->e.block.result = binary_expr ('-', e1, binary_expr ('*', e2, tmp2));
return e;
}
break;
}
}
return 0;
}
static expr_t *
check_precedence (int op, expr_t *e1, expr_t *e2)
{
if (e1->type == ex_uexpr && e1->e.expr.op == '!' && !e1->paren) {
if (options.traditional) {
if (op != AND && op != OR && op != '=') {
notice (e1, "precedence of `!' and `%s' inverted for "
"traditional code", get_op_string (op));
e1->e.expr.e1->paren = 1;
return unary_expr ('!', binary_expr (op, e1->e.expr.e1, e2));
}
} else if (op == '&' || op == '|') {
if (options.warnings.precedence)
warning (e1, "ambiguous logic. Suggest explicit parentheses "
"with expressions involving ! and %s",
get_op_string (op));
}
}
if (options.traditional) {
if (e2->type == ex_expr && !e2->paren) {
if (((op == '&' || op == '|')
&& (is_math_op (e2->e.expr.op) || is_compare (e2->e.expr.op)))
|| (op == '='
&& (e2->e.expr.op == OR || e2->e.expr.op == AND))) {
notice (e1, "precedence of `%s' and `%s' inverted for "
"traditional code", get_op_string (op),
get_op_string (e2->e.expr.op));
e1 = binary_expr (op, e1, e2->e.expr.e1);
e1->paren = 1;
return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2);
}
if (((op == EQ || op == NE) && is_compare (e2->e.expr.op))
|| (op == OR && e2->e.expr.op == AND)
|| (op == '|' && e2->e.expr.op == '&')) {
notice (e1, "precedence of `%s' raised to `%s' for "
"traditional code", get_op_string (op),
get_op_string (e2->e.expr.op));
e1 = binary_expr (op, e1, e2->e.expr.e1);
e1->paren = 1;
return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2);
}
} else if (e1->type == ex_expr && !e1->paren) {
if (((op == '&' || op == '|')
&& (is_math_op (e1->e.expr.op) || is_compare (e1->e.expr.op)))
|| (op == '='
&& (e1->e.expr.op == OR || e1->e.expr.op == AND))) {
notice (e1, "precedence of `%s' and `%s' inverted for "
"traditional code", get_op_string (op),
get_op_string (e1->e.expr.op));
e2 = binary_expr (op, e1->e.expr.e2, e2);
e2->paren = 1;
return binary_expr (e1->e.expr.op, e1->e.expr.e1, e2);
}
}
} else {
if (e2->type == ex_expr && !e2->paren) {
if ((op == '&' || op == '|' || op == '^')
&& (is_math_op (e2->e.expr.op)
|| is_compare (e2->e.expr.op))) {
if (options.warnings.precedence)
warning (e2, "suggest parentheses around %s in "
"operand of %c",
is_compare (e2->e.expr.op)
? "comparison"
: get_op_string (e2->e.expr.op),
op);
}
}
if (e1->type == ex_expr && !e1->paren) {
if ((op == '&' || op == '|' || op == '^')
&& (is_math_op (e1->e.expr.op)
|| is_compare (e1->e.expr.op))) {
if (options.warnings.precedence)
warning (e1, "suggest parentheses around %s in "
"operand of %c",
is_compare (e1->e.expr.op)
? "comparison"
: get_op_string (e1->e.expr.op),
op);
}
}
}
return 0;
}
static int is_call (expr_t *e)
{
return e->type == ex_block && e->e.block.is_call;
}
expr_t *
binary_expr (int op, expr_t *e1, expr_t *e2)
{
type_t *t1, *t2;
etype_t et1, et2;
expr_t *e;
expr_type_t *expr_type;
convert_name (e1);
e1 = convert_vector (e1);
// FIXME this is target-specific info and should not be in the
// expression tree
if ((e1->type == ex_expr || e1->type == ex_uexpr) && e1->e.expr.op == 'A'
&& is_call (e1->e.expr.e1)) {
// move the alias expression inside the block so the following check
// can detect the call and move the temp assignment into the block
expr_t *block = e1->e.expr.e1;
e1->e.expr.e1 = block->e.block.result;
block->e.block.result = e1;
e1 = block;
}
if (e1->type == ex_block && e1->e.block.is_call
&& has_function_call (e2) && e1->e.block.result) {
// the temp assignment needs to be insided the block so assignment
// code generation doesn't see it when applying right-associativity
expr_t *tmp = new_temp_def_expr (get_type (e1->e.block.result));
e = assign_expr (tmp, e1->e.block.result);
append_expr (e1, e);
e1->e.block.result = tmp;
}
if (e1->type == ex_error)
return e1;
convert_name (e2);
e2 = convert_vector (e2);
if (e2->type == ex_error)
return e2;
if (e1->type == ex_bool)
e1 = convert_from_bool (e1, get_type (e2));
if (e2->type == ex_bool)
e2 = convert_from_bool (e2, get_type (e1));
if ((e = check_precedence (op, e1, e2)))
return e;
t1 = get_type (e1);
t2 = get_type (e2);
if (!t1 || !t2)
internal_error (e1, "expr with no type");
if (op == EQ || op == NE) {
if (e1->type == ex_nil) {
t1 = t2;
convert_nil (e1, t1);
} else if (e2->type == ex_nil) {
t2 = t1;
convert_nil (e2, t2);
}
}
if (is_constant (e1) && is_double (t1) && e1->implicit && is_float (t2)) {
t1 = &type_float;
convert_double (e1);
}
if (is_constant (e2) && is_double (t2) && e2->implicit && is_float (t1)) {
t2 = &type_float;
convert_double (e2);
}
et1 = low_level_type (t1);
et2 = low_level_type (t2);
if (et1 >= ev_type_count || !binary_expr_types[et1])
return invalid_binary_expr(op, e1, e2);
if (et2 >= ev_type_count || !binary_expr_types[et1][et2])
return invalid_binary_expr(op, e1, e2);
expr_type = binary_expr_types[et1][et2];
while (expr_type->op && expr_type->op != op)
expr_type++;
if (!expr_type->op)
return invalid_binary_expr(op, e1, e2);
if (expr_type->a_cast)
e1 = cast_expr (expr_type->a_cast, e1);
if (expr_type->b_cast)
e2 = cast_expr (expr_type->b_cast, e2);
if (expr_type->process) {
return fold_constants (expr_type->process (op, e1, e2));
}
if ((e = reimplement_binary_expr (op, e1, e2)))
return fold_constants (e);
e = new_binary_expr (op, e1, e2);
e->e.expr.type = expr_type->type;
if (is_compare (op) || is_logic (op)) {
if (options.code.progsversion == PROG_ID_VERSION) {
e->e.expr.type = &type_float;
}
}
return fold_constants (e);
}