/* expr_unary.c expression construction and manipulations Copyright (C) 2001,2024 Bill Currie 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 #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/alloc.h" #include "QF/dstring.h" #include "QF/mathlib.h" #include "QF/va.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/algebra.h" #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/emit.h" #include "tools/qfcc/include/evaluate.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/function.h" #include "tools/qfcc/include/idstuff.h" #include "tools/qfcc/include/method.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/reloc.h" #include "tools/qfcc/include/rua-lang.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" typedef struct { int op; const type_t *result_type; const expr_t *(*process)(const expr_t *e); const expr_t *(*constant)(const expr_t *e); } unary_type_t; static const expr_t * string_not (const expr_t *e) { const char *s = expr_string (e); return new_int_expr (!s || !s[0], false); } static const expr_t * float_negate (const expr_t *e) { return new_float_expr (-expr_float (e), e->implicit); } static const expr_t * float_not (const expr_t *e) { return new_int_expr (!expr_float (e), false); } static const expr_t * float_bitnot (const expr_t *e) { return new_float_expr (~(int) expr_float (e), false); } static const expr_t * vector_negate (const expr_t *e) { vec3_t v; VectorNegate (expr_vector (e), v); return new_vector_expr (v); } static const expr_t * vector_not (const expr_t *e) { return new_int_expr (!VectorIsZero (expr_vector (e)), false); } static const expr_t * quat_negate (const expr_t *e) { quat_t q; QuatNegate (expr_quaternion (e), q); return new_vector_expr (q); } const expr_t * pointer_deref (const expr_t *e) { scoped_src_loc (e); auto new = new_unary_expr ('.', e); new->expr.type = get_type (e)->fldptr.type; return new; } static const expr_t * quat_not (const expr_t *e) { return new_int_expr (!QuatIsZero (expr_quaternion (e)), false); } static const expr_t * quat_conj (const expr_t *e) { quat_t q; QuatConj (expr_vector (e), q); return new_quaternion_expr (q); } static const expr_t * int_negate (const expr_t *e) { return new_int_expr (-expr_int (e), false); } static const expr_t * int_not (const expr_t *e) { return new_int_expr (!expr_int (e), e->implicit); } static const expr_t * int_bitnot (const expr_t *e) { return new_int_expr (~expr_int (e), e->implicit); } static const expr_t * uint_negate (const expr_t *e) { return new_uint_expr (-expr_uint (e)); } static const expr_t * uint_not (const expr_t *e) { return new_uint_expr (!expr_uint (e)); } static const expr_t * uint_bitnot (const expr_t *e) { return new_uint_expr (~expr_uint (e)); } static const expr_t * short_negate (const expr_t *e) { return new_short_expr (-expr_short (e)); } static const expr_t * short_not (const expr_t *e) { return new_short_expr (!expr_short (e)); } static const expr_t * short_bitnot (const expr_t *e) { return new_short_expr (~expr_short (e)); } static const expr_t * ushort_negate (const expr_t *e) { return new_ushort_expr (-expr_ushort (e)); } static const expr_t * ushort_not (const expr_t *e) { return new_ushort_expr (!expr_ushort (e)); } static const expr_t * ushort_bitnot (const expr_t *e) { return new_ushort_expr (~expr_ushort (e)); } static const expr_t * double_negate (const expr_t *e) { return new_double_expr (-expr_double (e), e->implicit); } static const expr_t * double_not (const expr_t *e) { return new_int_expr (!expr_double (e), false); } static const expr_t * double_bitnot (const expr_t *e) { return new_double_expr (~(pr_long_t) expr_double (e), false); } static const expr_t * long_negate (const expr_t *e) { return new_long_expr (-expr_long (e), e->implicit); } static const expr_t * long_not (const expr_t *e) { return new_int_expr (!expr_long (e), false); } static const expr_t * long_bitnot (const expr_t *e) { return new_long_expr (~expr_long (e), e->implicit); } static const expr_t * ulong_negate (const expr_t *e) { return new_ulong_expr (-expr_ulong (e)); } static const expr_t * ulong_not (const expr_t *e) { return new_int_expr (!expr_ulong (e), false); } static const expr_t * ulong_bitnot (const expr_t *e) { return new_ulong_expr (~expr_ulong (e)); } static unary_type_t string_u[] = { { .op = '!', .result_type = &type_bool, .constant = string_not, }, {} }; static unary_type_t float_u[] = { { .op = '-', .result_type = &type_float, .constant = float_negate, }, { .op = '!', .result_type = &type_bool, .constant = float_not, }, { .op = '~', .result_type = &type_float, .constant = float_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t vector_u[] = { { .op = '-', .result_type = &type_vector, .constant = vector_negate, }, { .op = '!', .result_type = &type_bool, .constant = vector_not, }, {} }; static unary_type_t entity_u[] = { { .op = '!', .result_type = &type_bool, }, {} }; static unary_type_t field_u[] = { { .op = '!', .result_type = &type_bool, }, {} }; static unary_type_t func_u[] = { { .op = '!', .result_type = &type_bool, }, {} }; static unary_type_t pointer_u[] = { { .op = '!', .result_type = &type_bool, }, { .op = '.', .process = pointer_deref, }, {} }; static unary_type_t quat_u[] = { { .op = '-', .result_type = &type_quaternion, .constant = quat_negate, }, { .op = '!', .result_type = &type_bool, .constant = quat_not, }, { .op = '~', .result_type = &type_quaternion, .constant = quat_conj, }, {} }; static unary_type_t int_u[] = { { .op = '-', .result_type = &type_int, .constant = int_negate, }, { .op = '!', .result_type = &type_bool, .constant = int_not, }, { .op = '~', .result_type = &type_int, .constant = int_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t uint_u[] = { { .op = '-', .result_type = &type_uint, .constant = uint_negate, }, { .op = '!', .result_type = &type_bool, .constant = uint_not, }, { .op = '~', .result_type = &type_uint, .constant = uint_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t short_u[] = { { .op = '-', .result_type = &type_short, .constant = short_negate, }, { .op = '!', .result_type = &type_bool, .constant = short_not, }, { .op = '~', .result_type = &type_short, .constant = short_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t ushort_u[] = { { .op = '-', .result_type = &type_ushort, .constant = ushort_negate, }, { .op = '!', .result_type = &type_bool, .constant = ushort_not, }, { .op = '~', .result_type = &type_ushort, .constant = ushort_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t double_u[] = { { .op = '-', .result_type = &type_double, .constant = double_negate, }, { .op = '!', .result_type = &type_lbool, .constant = double_not, }, { .op = '~', .result_type = &type_double, .constant = double_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t long_u[] = { { .op = '-', .result_type = &type_long, .constant = ulong_negate, }, { .op = '!', .result_type = &type_lbool, .constant = ulong_not, }, { .op = '~', .result_type = &type_long, .constant = ulong_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t ulong_u[] = { { .op = '-', .result_type = &type_ulong, .constant = long_negate, }, { .op = '!', .result_type = &type_lbool, .constant = long_not, }, { .op = '~', .result_type = &type_ulong, .constant = long_bitnot, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t algebra_u[] = { { .op = '-', .process = algebra_negate, }, { .op = '!', .process = algebra_dual, }, { .op = '~', .process = algebra_reverse, }, { .op = QC_REVERSE, .process = algebra_reverse, }, { .op = QC_DUAL, .process = algebra_dual, }, { .op = QC_UNDUAL, .process = algebra_undual, }, {} }; static unary_type_t *unary_expr_types[ev_type_count] = { [ev_string] = string_u, [ev_float] = float_u, [ev_vector] = vector_u, [ev_entity] = entity_u, [ev_field] = field_u, [ev_func] = func_u, [ev_ptr] = pointer_u, [ev_quaternion] = quat_u, [ev_int] = int_u, [ev_uint] = uint_u, [ev_short] = short_u, [ev_ushort] = ushort_u, [ev_double] = double_u, [ev_long] = long_u, [ev_ulong] = ulong_u, }; const expr_t * unary_expr (int op, const expr_t *e) { e = convert_name (e); if (e->type == ex_error) { return e; } if (is_reference (get_type (e))) { e = pointer_deref (e); } unary_type_t *unary_type = nullptr; auto t = get_type (e); if (op == '!' && e->type == ex_bool) { return new_bool_expr (e->boolean.false_list, e->boolean.true_list, e); } if (op == '+' && is_math (t)) { // unary + is a no-op return e; } if (is_algebra (t)) { unary_type = algebra_u; } else if (is_enum (t)) { unary_type = int_u; } else if (t->meta == ty_basic || is_handle (t)) { unary_type = unary_expr_types[t->type]; } if (!unary_type) { return error (e, "invalid type for unary expression"); } while (unary_type->op && unary_type->op != op) { unary_type++; } if (!unary_type->op) { return error (e, "invalid type for unary %s", get_op_string (op)); } if (op == '-' && e->type == ex_expr && e->expr.anticommute) { auto neg = new_expr (); *neg = *e; neg->expr.e1 = e->expr.e2; neg->expr.e2 = e->expr.e1; return neg; } if (unary_type->process) { return unary_type->process (e); } if (is_constant (e) && !unary_type->constant) { internal_error (e, "unexpected expression type"); } if (is_constant (e) && unary_type->constant) { return unary_type->constant (e); } auto result_type = t; if (unary_type->result_type) { result_type = unary_type->result_type; if (!is_handle (t)) { result_type = matrix_type (result_type, type_cols (t), type_rows (t)); } } if (result_type == &type_bool) { //FIXME support bool properly if (is_long (t) || is_ulong (t) || is_double (t)) { result_type = &type_long; } else { result_type = type_default; } } auto new = new_unary_expr (op, e); new->expr.type = result_type; return new; }