mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 23:32:09 +00:00
942ea22266
This fixes the internal error generated by the likes of `(sv_gravity * '0 0 1')` where sv_gravity is a float and `'0 0 1'` is an ivec3: the vector is promoted to vec3 first so that expanding sv_gravity is expanded to vec3 instead of ivec3 (which is not permitted for a float: expansion requires the destination base type to be the same as the source).
1160 lines
30 KiB
C
1160 lines
30 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 *result_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 *func_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_t *vector_compare (int op, expr_t *e1, expr_t *e2);
|
|
static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2);
|
|
static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2);
|
|
static expr_t *entity_compare (int op, expr_t *e1, expr_t *e2);
|
|
|
|
static expr_type_t string_string[] = {
|
|
{'+', &type_string},
|
|
{EQ, &type_int},
|
|
{NE, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{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_int},
|
|
{OR, &type_int},
|
|
{EQ, &type_int},
|
|
{NE, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t float_vector[] = {
|
|
{'*', .process = vector_scale },
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t float_quat[] = {
|
|
{'*', &type_quaternion},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t float_int[] = {
|
|
{'+', &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_int, 0, &type_float},
|
|
{NE, &type_int, 0, &type_float},
|
|
{LE, &type_int, 0, &type_float},
|
|
{GE, &type_int, 0, &type_float},
|
|
{LT, &type_int, 0, &type_float},
|
|
{GT, &type_int, 0, &type_float},
|
|
{0, 0}
|
|
};
|
|
#define float_uint float_int
|
|
#define float_short float_int
|
|
|
|
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[] = {
|
|
{'*', .process = vector_scale },
|
|
{'/', 0, 0, 0, inverse_multiply},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t vector_vector[] = {
|
|
{'+', &type_vector},
|
|
{'-', &type_vector},
|
|
{DOT, &type_vector},
|
|
{CROSS, &type_vector},
|
|
{'*', 0, 0, 0, vector_multiply},
|
|
{EQ, 0, 0, 0, vector_compare},
|
|
{NE, 0, 0, 0, vector_compare},
|
|
{0, 0}
|
|
};
|
|
|
|
#define vector_int vector_float
|
|
#define vector_uint vector_float
|
|
#define vector_short vector_float
|
|
|
|
static expr_type_t vector_double[] = {
|
|
{'*', &type_vector, 0, &type_float, vector_scale},
|
|
{'/', 0, 0, 0, inverse_multiply},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t entity_entity[] = {
|
|
{EQ, &type_int, 0, 0, entity_compare},
|
|
{NE, &type_int, 0, 0, entity_compare},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t field_field[] = {
|
|
{EQ, &type_int},
|
|
{NE, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t func_func[] = {
|
|
{EQ, 0, 0, 0, func_compare},
|
|
{NE, 0, 0, 0, func_compare},
|
|
{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_int[] = {
|
|
{'+', 0, 0, 0, pointer_arithmetic},
|
|
{'-', 0, 0, 0, pointer_arithmetic},
|
|
{0, 0}
|
|
};
|
|
#define pointer_uint pointer_int
|
|
#define pointer_short pointer_int
|
|
|
|
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_int},
|
|
{NE, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t quat_int[] = {
|
|
{'*', &type_quaternion, 0, &type_float},
|
|
{'/', 0, 0, 0, inverse_multiply},
|
|
{0, 0}
|
|
};
|
|
#define quat_uint quat_int
|
|
#define quat_short quat_int
|
|
|
|
static expr_type_t quat_double[] = {
|
|
{'*', &type_quaternion},
|
|
{'/', 0, 0, 0, inverse_multiply},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_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_int, 0, &type_int}, //FIXME?
|
|
{SHR, &type_int, 0, &type_int}, //FIXME?
|
|
{EQ, &type_int, &type_float, 0},
|
|
{NE, &type_int, &type_float, 0},
|
|
{LE, &type_int, &type_float, 0},
|
|
{GE, &type_int, &type_float, 0},
|
|
{LT, &type_int, &type_float, 0},
|
|
{GT, &type_int, &type_float, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_vector[] = {
|
|
{'*', &type_vector, &type_float, 0, vector_scale},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_pointer[] = {
|
|
{'+', 0, 0, 0, pointer_arithmetic},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_quat[] = {
|
|
{'*', &type_quaternion, &type_float, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_int[] = {
|
|
{'+', &type_int},
|
|
{'-', &type_int},
|
|
{'*', &type_int},
|
|
{'/', &type_int},
|
|
{'&', &type_int},
|
|
{'|', &type_int},
|
|
{'^', &type_int},
|
|
{'%', &type_int},
|
|
{MOD, &type_int},
|
|
{SHL, &type_int},
|
|
{SHR, &type_int},
|
|
{AND, &type_int},
|
|
{OR, &type_int},
|
|
{EQ, &type_int},
|
|
{NE, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_uint[] = {
|
|
{'+', &type_int, 0, &type_int},
|
|
{'-', &type_int, 0, &type_int},
|
|
{'*', &type_int, 0, &type_int},
|
|
{'/', &type_int, 0, &type_int},
|
|
{'&', &type_int, 0, &type_int},
|
|
{'|', &type_int, 0, &type_int},
|
|
{'^', &type_int, 0, &type_int},
|
|
{'%', &type_int, 0, &type_int},
|
|
{MOD, &type_int, 0, &type_int},
|
|
{SHL, &type_int, 0, &type_int},
|
|
{SHR, &type_int, 0, &type_int},
|
|
{EQ, &type_int, 0, &type_int},
|
|
{NE, &type_int, 0, &type_int},
|
|
{LE, &type_int, 0, &type_int},
|
|
{GE, &type_int, 0, &type_int},
|
|
{LT, &type_int, 0, &type_int},
|
|
{GT, &type_int, 0, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_short[] = {
|
|
{'+', &type_int, 0, &type_int},
|
|
{'-', &type_int, 0, &type_int},
|
|
{'*', &type_int, 0, &type_int},
|
|
{'/', &type_int, 0, &type_int},
|
|
{'&', &type_int, 0, &type_int},
|
|
{'|', &type_int, 0, &type_int},
|
|
{'^', &type_int, 0, &type_int},
|
|
{'%', &type_int, 0, &type_int},
|
|
{MOD, &type_int, 0, &type_int},
|
|
{SHL, &type_int, 0, &type_int},
|
|
{SHR, &type_int, 0, &type_int},
|
|
{EQ, &type_int, 0, &type_int},
|
|
{NE, &type_int, 0, &type_int},
|
|
{LE, &type_int, 0, &type_int},
|
|
{GE, &type_int, 0, &type_int},
|
|
{LT, &type_int, 0, &type_int},
|
|
{GT, &type_int, 0, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t int_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_int, &type_double, 0},
|
|
{NE, &type_int, &type_double, 0},
|
|
{LE, &type_int, &type_double, 0},
|
|
{GE, &type_int, &type_double, 0},
|
|
{LT, &type_int, &type_double, 0},
|
|
{GT, &type_int, &type_double, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
#define uint_float int_float
|
|
#define uint_vector int_vector
|
|
#define uint_pointer int_pointer
|
|
#define uint_quat int_quat
|
|
|
|
static expr_type_t uint_int[] = {
|
|
{'+', &type_int, &type_int, &type_int },
|
|
{'-', &type_int, &type_int, &type_int },
|
|
{'*', &type_int, &type_int, &type_int },
|
|
{'/', &type_int, &type_int, &type_int },
|
|
{'&', &type_int, &type_int, &type_int },
|
|
{'|', &type_int, &type_int, &type_int },
|
|
{'^', &type_int, &type_int, &type_int },
|
|
{'%', &type_int, &type_int, &type_int },
|
|
{MOD, &type_int, &type_int, &type_int },
|
|
{SHL, &type_uint, &type_int, &type_int },
|
|
{SHR, &type_uint, 0, &type_int },
|
|
{EQ, &type_int, &type_int, &type_int },
|
|
{NE, &type_int, &type_int, &type_int },
|
|
{LE, &type_int, &type_int, &type_int },
|
|
{GE, &type_int, &type_int, &type_int },
|
|
{LT, &type_int, &type_int, &type_int },
|
|
{GT, &type_int, &type_int, &type_int },
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t uint_uint[] = {
|
|
{'+', &type_uint},
|
|
{'-', &type_uint},
|
|
{'*', &type_uint},
|
|
{'/', &type_uint},
|
|
{'&', &type_uint},
|
|
{'|', &type_uint},
|
|
{'^', &type_uint},
|
|
{'%', &type_uint},
|
|
{MOD, &type_uint},
|
|
{SHL, &type_uint},
|
|
{SHR, &type_uint},
|
|
{EQ, &type_int, &type_int, &type_int},
|
|
{NE, &type_int, &type_int, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{0, 0}
|
|
};
|
|
#define uint_short uint_int
|
|
#define uint_double int_double
|
|
|
|
#define short_float int_float
|
|
#define short_vector int_vector
|
|
#define short_pointer int_pointer
|
|
#define short_quat int_quat
|
|
|
|
static expr_type_t short_int[] = {
|
|
{'+', &type_int, &type_int, 0},
|
|
{'-', &type_int, &type_int, 0},
|
|
{'*', &type_int, &type_int, 0},
|
|
{'/', &type_int, &type_int, 0},
|
|
{'&', &type_int, &type_int, 0},
|
|
{'|', &type_int, &type_int, 0},
|
|
{'^', &type_int, &type_int, 0},
|
|
{'%', &type_int, &type_int, 0},
|
|
{MOD, &type_int, &type_int, 0},
|
|
{SHL, &type_short},
|
|
{SHR, &type_short},
|
|
{EQ, &type_int, &type_int, 0},
|
|
{NE, &type_int, &type_int, 0},
|
|
{LE, &type_int, &type_int, 0},
|
|
{GE, &type_int, &type_int, 0},
|
|
{LT, &type_int, &type_int, 0},
|
|
{GT, &type_int, &type_int, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t short_uint[] = {
|
|
{'+', &type_uint, &type_uint, 0},
|
|
{'-', &type_uint, &type_uint, 0},
|
|
{'*', &type_uint, &type_uint, 0},
|
|
{'/', &type_uint, &type_uint, 0},
|
|
{'&', &type_uint, &type_uint, 0},
|
|
{'|', &type_uint, &type_uint, 0},
|
|
{'^', &type_uint, &type_uint, 0},
|
|
{'%', &type_uint, &type_uint, 0},
|
|
{MOD, &type_uint, &type_uint, 0},
|
|
{SHL, &type_short},
|
|
{SHR, &type_short},
|
|
{EQ, &type_int, &type_uint, 0},
|
|
{NE, &type_int, &type_uint, 0},
|
|
{LE, &type_int, &type_uint, 0},
|
|
{GE, &type_int, &type_uint, 0},
|
|
{LT, &type_int, &type_uint, 0},
|
|
{GT, &type_int, &type_uint, 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_int},
|
|
{NE, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{0, 0}
|
|
};
|
|
#define short_double int_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, &type_float, 0, vector_scale},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t double_quat[] = {
|
|
{'*', &type_quaternion},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t double_int[] = {
|
|
{'+', &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_uint double_int
|
|
#define double_short double_int
|
|
|
|
static expr_type_t double_double[] = {
|
|
{'+', &type_double},
|
|
{'-', &type_double},
|
|
{'*', &type_double},
|
|
{'/', &type_double},
|
|
{'%', &type_double},
|
|
{MOD, &type_double},
|
|
{EQ, &type_int},
|
|
{NE, &type_int},
|
|
{LE, &type_int},
|
|
{GE, &type_int},
|
|
{LT, &type_int},
|
|
{GT, &type_int},
|
|
{0, 0}
|
|
};
|
|
|
|
static expr_type_t *string_x[ev_type_count] = {
|
|
[ev_string] = string_string,
|
|
};
|
|
|
|
static expr_type_t *float_x[ev_type_count] = {
|
|
[ev_float] = float_float,
|
|
[ev_vector] = float_vector,
|
|
[ev_quaternion] = float_quat,
|
|
[ev_int] = float_int,
|
|
[ev_uint] = float_uint,
|
|
[ev_short] = float_short,
|
|
[ev_double] = float_double,
|
|
};
|
|
|
|
static expr_type_t *vector_x[ev_type_count] = {
|
|
[ev_float] = vector_float,
|
|
[ev_vector] = vector_vector,
|
|
[ev_int] = vector_int,
|
|
[ev_uint] = vector_uint,
|
|
[ev_short] = vector_short,
|
|
[ev_double] = vector_double,
|
|
};
|
|
|
|
static expr_type_t *entity_x[ev_type_count] = {
|
|
[ev_entity] = entity_entity,
|
|
};
|
|
|
|
static expr_type_t *field_x[ev_type_count] = {
|
|
[ev_field] = field_field,
|
|
};
|
|
|
|
static expr_type_t *func_x[ev_type_count] = {
|
|
[ev_func] = func_func,
|
|
};
|
|
|
|
static expr_type_t *pointer_x[ev_type_count] = {
|
|
[ev_ptr] = pointer_pointer,
|
|
[ev_int] = pointer_int,
|
|
[ev_uint] = pointer_uint,
|
|
[ev_short] = pointer_short,
|
|
};
|
|
|
|
static expr_type_t *quat_x[ev_type_count] = {
|
|
[ev_float] = quat_float,
|
|
[ev_vector] = quat_vector,
|
|
[ev_quaternion] = quat_quat,
|
|
[ev_int] = quat_int,
|
|
[ev_uint] = quat_uint,
|
|
[ev_short] = quat_short,
|
|
[ev_double] = quat_double,
|
|
};
|
|
|
|
static expr_type_t *int_x[ev_type_count] = {
|
|
[ev_float] = int_float,
|
|
[ev_vector] = int_vector,
|
|
[ev_ptr] = int_pointer,
|
|
[ev_quaternion] = int_quat,
|
|
[ev_int] = int_int,
|
|
[ev_uint] = int_uint,
|
|
[ev_short] = int_short,
|
|
[ev_double] = int_double,
|
|
};
|
|
|
|
static expr_type_t *uint_x[ev_type_count] = {
|
|
[ev_float] = uint_float,
|
|
[ev_vector] = uint_vector,
|
|
[ev_ptr] = uint_pointer,
|
|
[ev_quaternion] = uint_quat,
|
|
[ev_int] = uint_int,
|
|
[ev_uint] = uint_uint,
|
|
[ev_short] = uint_short,
|
|
[ev_double] = uint_double,
|
|
};
|
|
|
|
static expr_type_t *short_x[ev_type_count] = {
|
|
[ev_float] = short_float,
|
|
[ev_vector] = short_vector,
|
|
[ev_ptr] = short_pointer,
|
|
[ev_quaternion] = short_quat,
|
|
[ev_int] = short_int,
|
|
[ev_uint] = short_uint,
|
|
[ev_short] = short_short,
|
|
[ev_double] = short_double,
|
|
};
|
|
|
|
static expr_type_t *double_x[ev_type_count] = {
|
|
[ev_float] = double_float,
|
|
[ev_vector] = double_vector,
|
|
[ev_quaternion] = double_quat,
|
|
[ev_int] = double_int,
|
|
[ev_uint] = double_uint,
|
|
[ev_short] = double_short,
|
|
[ev_double] = double_double,
|
|
};
|
|
|
|
static expr_type_t **binary_expr_types[ev_type_count] = {
|
|
[ev_string] = string_x,
|
|
[ev_float] = float_x,
|
|
[ev_vector] = vector_x,
|
|
[ev_entity] = entity_x,
|
|
[ev_field] = field_x,
|
|
[ev_func] = func_x,
|
|
[ev_ptr] = pointer_x,
|
|
[ev_quaternion] = quat_x,
|
|
[ev_int] = int_x,
|
|
[ev_uint] = uint_x,
|
|
[ev_short] = short_x,
|
|
[ev_double] = double_x
|
|
};
|
|
|
|
// supported operators for scalar-vector expressions
|
|
static int scalar_vec_ops[] = { '*', '/', '%', MOD, 0 };
|
|
static expr_t *
|
|
convert_scalar (expr_t *scalar, int op, expr_t *vec)
|
|
{
|
|
int *s_op = scalar_vec_ops;
|
|
while (*s_op && *s_op != op) {
|
|
s_op++;
|
|
}
|
|
if (!*s_op) {
|
|
return 0;
|
|
}
|
|
|
|
// expand the scalar to a vector of the same width as vec
|
|
type_t *vec_type = get_type (vec);
|
|
|
|
if (is_constant (scalar)) {
|
|
for (int i = 1; i < type_width (get_type (vec)); i++) {
|
|
expr_t *s = copy_expr (scalar);
|
|
s->next = scalar;
|
|
scalar = s;
|
|
}
|
|
return new_vector_list (scalar);
|
|
}
|
|
|
|
return new_extend_expr (scalar, vec_type, 2);//2 = copy
|
|
}
|
|
|
|
static expr_t *
|
|
pointer_arithmetic (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
type_t *t1 = get_type (e1);
|
|
type_t *t2 = get_type (e2);
|
|
expr_t *ptr = 0;
|
|
expr_t *offset = 0;
|
|
expr_t *psize;
|
|
type_t *ptype = 0;
|
|
|
|
if (!is_ptr (t1) && !is_ptr (t2)) {
|
|
internal_error (e1, "pointer arithmetic on non-pointers");
|
|
}
|
|
if (is_ptr (t1) && is_ptr (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_int, e1);
|
|
e2 = cast_expr (&type_int, e2);
|
|
psize = new_int_expr (type_size (t1->t.fldptr.type));
|
|
return binary_expr ('/', binary_expr ('-', e1, e2), psize);
|
|
} else if (is_ptr (t1)) {
|
|
offset = cast_expr (&type_int, e2);
|
|
ptr = e1;
|
|
ptype = t1;
|
|
} else if (is_ptr (t2)) {
|
|
offset = cast_expr (&type_int, e1);
|
|
ptr = e2;
|
|
ptype = t2;
|
|
}
|
|
// op is known to be + or -
|
|
psize = new_int_expr (type_size (ptype->t.fldptr.type));
|
|
offset = unary_expr (op, binary_expr ('*', offset, psize));
|
|
return offset_pointer_expr (ptr, offset);
|
|
}
|
|
|
|
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));
|
|
}
|
|
if (options.code.progsversion < PROG_VERSION) {
|
|
e = new_binary_expr (op, e1, e2);
|
|
} else {
|
|
e = new_binary_expr (op, cast_expr (&type_int, e1),
|
|
cast_expr (&type_int, e2));
|
|
}
|
|
e->e.expr.type = &type_int;
|
|
return e;
|
|
}
|
|
|
|
static expr_t *
|
|
func_compare (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
expr_t *e;
|
|
|
|
if (options.code.progsversion < PROG_VERSION) {
|
|
e = new_binary_expr (op, e1, e2);
|
|
} else {
|
|
e = new_binary_expr (op, new_alias_expr (&type_int, e1),
|
|
new_alias_expr (&type_int, e2));
|
|
}
|
|
e->e.expr.type = &type_int;
|
|
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 *
|
|
vector_compare (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
if (options.code.progsversion < PROG_VERSION) {
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_int;
|
|
return e;
|
|
}
|
|
int hop = op == EQ ? '&' : '|';
|
|
e1 = new_alias_expr (&type_vec3, e1);
|
|
e2 = new_alias_expr (&type_vec3, e2);
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_ivec3;
|
|
return new_horizontal_expr (hop, e, &type_int);
|
|
}
|
|
|
|
static expr_t *vector_multiply (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
expr_t *e = new_binary_expr ('*', e1, e2);
|
|
if (options.math.vector_mult == DOT) {
|
|
// vector * vector is dot product in v6 progs (ick)
|
|
e->e.expr.op = DOT;
|
|
if (options.code.progsversion == PROG_VERSION) {
|
|
e->e.expr.type = &type_vector;
|
|
e = new_alias_expr (&type_float, e);
|
|
} else {
|
|
e->e.expr.type = &type_float;
|
|
}
|
|
} else {
|
|
// component-wise multiplication
|
|
e->e.expr.type = &type_vector;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
static expr_t *vector_scale (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
// Ensure the expression is always vector * scalar. The operation is
|
|
// always commutative, and the Ruamoko ISA supports only vector * scalar
|
|
// (though v6 does support scalar * vector, one less if).
|
|
if (is_scalar (get_type (e1))) {
|
|
expr_t *t = e1;
|
|
e1 = e2;
|
|
e2 = t;
|
|
}
|
|
expr_t *e = new_binary_expr (SCALE, e1, e2);
|
|
e->e.expr.type = get_type (e1);
|
|
return e;
|
|
}
|
|
|
|
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;
|
|
e1 = cast_expr (t1, e1);
|
|
}
|
|
if (is_float (t1) && is_constant (e2) && e2->implicit && is_double (t2)) {
|
|
t2 = &type_float;
|
|
e2 = cast_expr (t2, 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 int");
|
|
}
|
|
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 int and double");
|
|
}
|
|
e1 = cast_expr (&type_double, e1);
|
|
}
|
|
e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_int;
|
|
return e;
|
|
}
|
|
|
|
static expr_t *
|
|
entity_compare (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
if (options.code.progsversion == PROG_VERSION) {
|
|
e1 = new_alias_expr (&type_int, e1);
|
|
e2 = new_alias_expr (&type_int, e2);
|
|
}
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_int;
|
|
return e;
|
|
}
|
|
|
|
#define invalid_binary_expr(_op, _e1, _e2) \
|
|
_invalid_binary_expr(_op, _e1, _e2, __FILE__, __LINE__)
|
|
static expr_t *
|
|
_invalid_binary_expr (int op, expr_t *e1, expr_t *e2,
|
|
const char *file, int line)
|
|
{
|
|
type_t *t1, *t2;
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
return _error (e1, file, line, "invalid binary expression: %s %s %s",
|
|
get_type_string (t1), get_op_string (op),
|
|
get_type_string (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) {
|
|
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)))
|
|
|| (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)))
|
|
|| (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;
|
|
}
|
|
|
|
static type_t *
|
|
promote_type (type_t *dst, type_t *src)
|
|
{
|
|
if (is_vector (dst) || is_quaternion (dst)) {
|
|
return dst;
|
|
}
|
|
return vector_type (base_type (dst), type_width (src));
|
|
}
|
|
|
|
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);
|
|
// FIXME this is target-specific info and should not be in the
|
|
// expression tree
|
|
if (e1->type == ex_alias && is_call (e1->e.alias.expr)) {
|
|
// 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.alias.expr;
|
|
e1->e.alias.expr = 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);
|
|
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;
|
|
e1 = cast_expr (t1, e1);
|
|
}
|
|
if (is_constant (e2) && is_double (t2) && e2->implicit && is_float (t1)) {
|
|
t2 = &type_float;
|
|
e2 = cast_expr (t2, 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);
|
|
|
|
if ((t1->width > 1 || t2->width > 1)) {
|
|
// vector/quaternion and scalar won't get here as vector and quaternion
|
|
// are distict types with type.width == 1, but vector and vec3 WILL get
|
|
// here because of vec3 being float{3}
|
|
if (t1 != t2) {
|
|
type_t *pt1 = t1;
|
|
type_t *pt2 = t2;
|
|
if (is_float (base_type (t1)) && is_double (base_type (t2))
|
|
&& e2->implicit) {
|
|
pt2 = promote_type (t1, t2);
|
|
} else if (is_double (base_type (t1)) && is_float (base_type (t2))
|
|
&& e1->implicit) {
|
|
pt1 = promote_type (t2, t1);
|
|
} else if (type_promotes (base_type (t1), base_type (t2))) {
|
|
pt2 = promote_type (t1, t2);
|
|
} else if (type_promotes (base_type (t2), base_type (t1))) {
|
|
pt1 = promote_type (t2, t1);
|
|
} else if (base_type (t1) == base_type (t2)) {
|
|
if (is_vector (t1) || is_quaternion (t1)) {
|
|
pt2 = t1;
|
|
} else if (is_vector (t2) || is_quaternion (t2)) {
|
|
pt1 = t2;
|
|
}
|
|
} else {
|
|
debug (e1, "%d %d\n", e1->implicit, e2->implicit);
|
|
return invalid_binary_expr (op, e1, e2);
|
|
}
|
|
if (pt1 != t1) {
|
|
e1 = cast_expr (pt1, e1);
|
|
t1 = pt1;
|
|
}
|
|
if (pt2 != t2) {
|
|
e2 = cast_expr (pt2, e2);
|
|
t2 = pt2;
|
|
}
|
|
}
|
|
if (type_width (t1) == 1) {
|
|
// scalar op vec
|
|
if (!(e = convert_scalar (e1, op, e2))) {
|
|
return invalid_binary_expr (op, e1, e2);
|
|
}
|
|
e1 = e;
|
|
t1 = get_type (e1);
|
|
}
|
|
if (type_width (t2) == 1) {
|
|
// vec op scalar
|
|
if (!(e = convert_scalar (e2, op, e1))) {
|
|
return invalid_binary_expr (op, e1, e2);
|
|
}
|
|
e2 = e;
|
|
t2 = get_type (e2);
|
|
}
|
|
if (type_width (t1) != type_width (t2)) {
|
|
// vec op vec of different widths
|
|
return invalid_binary_expr (op, e1, e2);
|
|
}
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
et1 = low_level_type (t1);
|
|
et2 = low_level_type (t2);
|
|
// both widths are the same at this point
|
|
if (t1->width > 1) {
|
|
e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = t1;
|
|
return e;
|
|
}
|
|
}
|
|
|
|
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->result_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);
|
|
}
|