/* expr.c expression construction and manipulations Copyright (C) 2001 Bill Currie Author: Bill Currie Date: 2001/06/15 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((used)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include #include "qfcc.h" #include "class.h" #include "def.h" #include "defspace.h" #include "emit.h" #include "expr.h" #include "function.h" #include "idstuff.h" #include "immediate.h" #include "method.h" #include "options.h" #include "reloc.h" #include "strpool.h" #include "struct.h" #include "symtab.h" #include "type.h" #include "qc-parse.h" static expr_t *free_exprs; int lineno_base; type_t *ev_types[ev_type_count] = { &type_void, &type_string, &type_float, &type_vector, &type_entity, &type_field, &type_function, &type_pointer, &type_quaternion, &type_integer, &type_short, &type_void, // FIXME what type? }; type_t * get_type (expr_t *e) { switch (e->type) { case ex_label: case ex_error: return 0; // something went very wrong case ex_bool: if (options.code.progsversion == PROG_ID_VERSION) return &type_float; return &type_integer; case ex_nil: case ex_state: return &type_void; case ex_block: if (e->e.block.result) return get_type (e->e.block.result); return &type_void; case ex_expr: case ex_uexpr: return e->e.expr.type; case ex_symbol: return e->e.symbol->type; case ex_temp: return e->e.temp.type; case ex_value: if (e->e.value.type == ev_pointer) return pointer_type (e->e.value.v.pointer.type); if (e->e.value.type == ev_integer && options.code.progsversion == PROG_ID_VERSION) { e->e.value.type = ev_float; e->e.value.v.float_val = e->e.value.v.integer_val; } return ev_types[e->e.value.type]; } return 0; } etype_t extract_type (expr_t *e) { type_t *type = get_type (e); if (type) return type->type; return ev_type_count; } const char * get_op_string (int op) { switch (op) { case PAS: return ".="; case OR: return "||"; case AND: return "&&"; case EQ: return "=="; case NE: return "!="; case LE: return "<="; case GE: return ">="; case LT: return "<"; case GT: return ">"; case '=': return "="; case '+': return "+"; case '-': return "-"; case '*': return "*"; case '/': return "/"; case '%': return "%"; case '&': return "&"; case '|': return "|"; case '^': return "^"; case '~': return "~"; case '!': return "!"; case SHL: return "<<"; case SHR: return ">>"; case '.': return "."; case 'i': return ""; case 'n': return ""; case IFBE: return ""; case IFB: return ""; case IFAE: return ""; case IFA: return ""; case 'g': return ""; case 'r': return ""; case 'b': return ""; case 's': return ""; case 'c': return ""; case 'C': return ""; case 'M': return ""; default: return "unknown"; } } expr_t * type_mismatch (expr_t *e1, expr_t *e2, int op) { dstring_t *t1 = dstring_newstr (); dstring_t *t2 = dstring_newstr (); print_type_str (t1, get_type (e1)); print_type_str (t2, get_type (e2)); e1 = error (e1, "type mismatch: %s %s %s", t1->str, get_op_string (op), t2->str); dstring_delete (t1); dstring_delete (t2); return e1; } expr_t * param_mismatch (expr_t *e, int param, const char *fn, type_t *t1, type_t *t2) { dstring_t *s1 = dstring_newstr (); dstring_t *s2 = dstring_newstr (); print_type_str (s1, t1); print_type_str (s2, t2); e = error (e, "type mismatch for parameter %d of %s: %s %s", param, fn, s1->str, s2->str); dstring_delete (s1); dstring_delete (s2); return e; } expr_t * cast_error (expr_t *e, type_t *t1, type_t *t2) { dstring_t *s1 = dstring_newstr (); dstring_t *s2 = dstring_newstr (); print_type_str (s1, t1); print_type_str (s2, t2); e = error (e, "can not cast from %s to %s", s1->str, s2->str); dstring_delete (s1); dstring_delete (s2); return e; } expr_t * test_error (expr_t *e, type_t *t) { dstring_t *s = dstring_newstr (); print_type_str (s, t); e = error (e, "%s cannot be tested", s->str); dstring_delete (s); return e; } expr_t * inc_users (expr_t *e) { if (e && e->type == ex_temp) e->e.temp.users++; else if (e && e->type == ex_block) inc_users (e->e.block.result); return e; } expr_t * dec_users (expr_t *e) { if (e && e->type == ex_temp) e->e.temp.users--; else if (e && e->type == ex_block) dec_users (e->e.block.result); return e; } expr_t * new_expr (void) { expr_t *e; ALLOC (16384, expr_t, exprs, e); e->line = pr.source_line; e->file = pr.source_file; return e; } expr_t * copy_expr (expr_t *e) { expr_t *n; expr_t *t; if (!e) return 0; switch (e->type) { case ex_error: case ex_symbol: case ex_nil: case ex_value: // nothing to do here n = new_expr (); *n = *e; return n; case ex_state: return new_state_expr (copy_expr (e->e.state.frame), copy_expr (e->e.state.think), copy_expr (e->e.state.step)); case ex_bool: n = new_expr (); *n = *e; if (e->e.bool.true_list) { int count = e->e.bool.true_list->size; size_t size = (size_t)&((ex_list_t *) 0)->e[count]; n->e.bool.true_list = malloc (size); while (count--) n->e.bool.true_list->e[count] = copy_expr ( e->e.bool.true_list->e[count]); } if (e->e.bool.false_list) { int count = e->e.bool.false_list->size; size_t size = (size_t)&((ex_list_t *) 0)->e[count]; n->e.bool.false_list = malloc (size); while (count--) n->e.bool.false_list->e[count] = copy_expr ( e->e.bool.false_list->e[count]); } n->e.bool.e = copy_expr (e->e.bool.e); return n; case ex_label: /// Create a fresh label return new_label_expr (); case ex_block: n = new_expr (); *n = *e; n->e.block.head = 0; n->e.block.tail = &n->e.block.head; n->e.block.result = 0; for (t = e->e.block.head; t; t = t->next) { if (t == e->e.block.result) { n->e.block.result = copy_expr (t); append_expr (n, n->e.block.result); } else { append_expr (n, copy_expr (t)); } } if (e->e.block.result && !n->e.block.result) { error (e, "internal: bogus block result?"); abort (); } break; case ex_expr: n = new_expr (); *n = *e; n->e.expr.e1 = copy_expr (e->e.expr.e1); n->e.expr.e2 = copy_expr (e->e.expr.e2); return n; case ex_uexpr: n = new_expr (); *n = *e; n->e.expr.e1 = copy_expr (e->e.expr.e1); return n; case ex_temp: n = new_expr (); *n = *e; n->e.temp.expr = copy_expr (e->e.temp.expr); e->e.temp.users = 0; //FIXME? return n; } error (e, "internal: invalid expression"); abort (); } const char * new_label_name (void) { static int label = 0; int lnum = ++label; const char *fname = current_func->sym->name; char *lname; lname = nva ("$%s_%d", fname, lnum); SYS_CHECKMEM (lname); return lname; } #if 0 static expr_t * new_error_expr (void) { expr_t *e = new_expr (); e->type = ex_error; return e; } #endif expr_t * new_state_expr (expr_t *frame, expr_t *think, expr_t *step) { expr_t *s = new_expr (); s->type = ex_state; s->e.state.frame = frame; s->e.state.think = think; s->e.state.step = step; return s; } expr_t * new_bool_expr (ex_list_t *true_list, ex_list_t *false_list, expr_t *e) { expr_t *b = new_expr (); b->type = ex_bool; b->e.bool.true_list = true_list; b->e.bool.false_list = false_list; b->e.bool.e = e; return b; } expr_t * new_label_expr (void) { expr_t *l = new_expr (); l->type = ex_label; l->e.label.name = new_label_name (); l->e.label.next = pr.labels; pr.labels = &l->e.label; return l; } expr_t * new_block_expr (void) { expr_t *b = new_expr (); b->type = ex_block; b->e.block.head = 0; b->e.block.tail = &b->e.block.head; return b; } expr_t * new_binary_expr (int op, expr_t *e1, expr_t *e2) { expr_t *e = new_expr (); if (e1->type == ex_error) return e1; if (e2 && e2->type == ex_error) return e2; inc_users (e1); inc_users (e2); e->type = ex_expr; e->e.expr.op = op; e->e.expr.e1 = e1; e->e.expr.e2 = e2; return e; } expr_t * new_unary_expr (int op, expr_t *e1) { expr_t *e = new_expr (); if (e1 && e1->type == ex_error) return e1; inc_users (e1); e->type = ex_uexpr; e->e.expr.op = op; e->e.expr.e1 = e1; return e; } expr_t * new_symbol_expr (symbol_t *symbol) { expr_t *e = new_expr (); e->type = ex_symbol; e->e.symbol = symbol; return e; } expr_t * new_temp_def_expr (type_t *type) { expr_t *e = new_expr (); e->type = ex_temp; e->e.temp.type = type; return e; } expr_t * new_nil_expr (void) { expr_t *e = new_expr (); e->type = ex_nil; return e; } expr_t * new_name_expr (const char *name) { expr_t *e = new_expr (); symbol_t *sym; sym = symtab_lookup (current_symtab, name); if (!sym) return error (0, "undefined symbol %s", name); e->type = ex_symbol; e->e.symbol = sym; return e; } expr_t * new_string_expr (const char *string_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_string; e->e.value.v.string_val = string_val; return e; } expr_t * new_float_expr (float float_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_float; e->e.value.v.float_val = float_val; return e; } expr_t * new_vector_expr (const float *vector_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_vector; VectorCopy (vector_val, e->e.value.v.vector_val); return e; } expr_t * new_entity_expr (int entity_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_entity; e->e.value.v.entity_val = entity_val; return e; } expr_t * new_field_expr (int field_val, type_t *type, def_t *def) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_field; e->e.value.v.pointer.val = field_val; e->e.value.v.pointer.type = type; e->e.value.v.pointer.def = def; return e; } expr_t * new_func_expr (int func_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_func; e->e.value.v.func_val = func_val; return e; } expr_t * new_pointer_expr (int val, type_t *type, def_t *def) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_pointer; e->e.value.v.pointer.val = val; e->e.value.v.pointer.type = type; e->e.value.v.pointer.def = def; return e; } expr_t * new_quaternion_expr (const float *quaternion_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_quat; QuatCopy (quaternion_val, e->e.value.v.quaternion_val); return e; } expr_t * new_integer_expr (int integer_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_integer; e->e.value.v.integer_val = integer_val; return e; } expr_t * new_short_expr (short short_val) { expr_t *e = new_expr (); e->type = ex_value; e->e.value.type = ev_short; e->e.value.v.short_val = short_val; return e; } int is_constant (expr_t *e) { if (e->type == ex_nil || e->type == ex_value || (e->type == ex_symbol && e->e.symbol->sy_type == sy_const)) return 1; return 0; } int is_string_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_string) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_string) return 1; return 0; } const char * expr_string (expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->e.value.type == ev_string) return e->e.value.v.string_val; //if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const // && e->e.symbol->type->type == ev_string) // return e->e.symbol->s.value; internal_error (e, "not a string constant"); } int is_float_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_float) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_float) return 1; return 0; } float expr_float (expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->e.value.type == ev_float) return e->e.value.v.float_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_float) return e->e.symbol->s.value.v.float_val; internal_error (e, "not a float constant"); } int is_vector_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_vector) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_vector) return 1; return 0; } const float * expr_vector (expr_t *e) { if (e->type == ex_nil) return vec3_origin; if (e->type == ex_value && e->e.value.type == ev_vector) return e->e.value.v.vector_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_vector) return e->e.symbol->s.value.v.vector_val; internal_error (e, "not a vector constant"); } int is_quaternion_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_quat) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_quat) return 1; return 0; } const float * expr_quaternion (expr_t *e) { if (e->type == ex_nil) return quat_origin; if (e->type == ex_value && e->e.value.type == ev_quat) return e->e.value.v.quaternion_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_quat) return e->e.symbol->s.value.v.quaternion_val; internal_error (e, "not a quaternion constant"); } int is_integer_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_integer) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_integer) return 1; return 0; } int expr_integer (expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->e.value.type == ev_integer) return e->e.value.v.integer_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_integer) return e->e.symbol->s.value.v.integer_val; internal_error (e, "not an integer constant"); } int is_short_val (expr_t *e) { if (e->type == ex_nil) return 1; if (e->type == ex_value && e->e.value.type == ev_short) return 1; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_short) return 1; return 0; } short expr_short (expr_t *e) { if (e->type == ex_nil) return 0; if (e->type == ex_value && e->e.value.type == ev_short) return e->e.value.v.short_val; if (e->type == ex_symbol && e->e.symbol->sy_type == sy_const && e->e.symbol->type->type == ev_short) return e->e.symbol->s.value.v.short_val; internal_error (e, "not a short constant"); } expr_t * new_bind_expr (expr_t *e1, expr_t *e2) { expr_t *e; if (!e2 || e2->type != ex_temp) { internal_error (e1, 0); } e = new_expr (); e->type = ex_expr; e->e.expr.op = 'b'; e->e.expr.e1 = e1; e->e.expr.e2 = e2; e->e.expr.type = get_type (e2); return e; } expr_t * new_self_expr (void) { symbol_t *sym = symtab_lookup (pr.symtab, ".self"); if (!sym) { sym = new_symbol_type (".self", &type_entity); symtab_addsymbol (pr.symtab, sym); } return new_symbol_expr (sym); } expr_t * new_this_expr (void) { symbol_t *sym = symtab_lookup (pr.symtab, ".this"); if (!sym) { sym = new_symbol_type (".this", field_type (&type_id)); symtab_addsymbol (pr.symtab, sym); } return new_symbol_expr (sym); } static expr_t * param_expr (const char *name, type_t *type) { symbol_t *sym = symtab_lookup (pr.symtab, name); expr_t *sym_expr; sym_expr = new_symbol_expr (sym); return unary_expr ('.', address_expr (sym_expr, 0, type)); } expr_t * new_ret_expr (type_t *type) { return param_expr (".return", type); } expr_t * new_param_expr (type_t *type, int num) { return param_expr (va (".param_%d", num), type); } expr_t * new_move_expr (expr_t *e1, expr_t *e2, type_t *type) { expr_t *e = new_binary_expr ('M', e1, e2); e->e.expr.type = type; return e; } expr_t * append_expr (expr_t *block, expr_t *e) { if (block->type != ex_block) abort (); if (!e || e->type == ex_error) return block; if (e->next) { error (e, "append_expr: expr loop detected"); abort (); } *block->e.block.tail = e; block->e.block.tail = &e->next; return block; } static void _print_expr (expr_t *e, int level, int id) { const char *label = "???"; const char *shape = "ellipse"; int indent = level * 2 + 2; if (!e) { printf ("%*se_%p [label=\"(null)\"];\n", indent, "", e); return; } if (e->printid == id) // already printed this expression return; e->printid = id; switch (e->type) { case ex_error: label = "(error)"; break; case ex_state: _print_expr (e->e.state.frame, level, id); _print_expr (e->e.state.think, level, id); if (e->e.state.step) _print_expr (e->e.state.step, level, id); printf ("%*se_%p:f -> e_%p;\n", indent, "", e, e->e.state.frame); printf ("%*se_%p:t -> e_%p;\n", indent, "", e, e->e.state.think); if (e->e.state.step) printf ("%*se_%p:s -> e_%p;\n", indent, "", e, e->e.state.step); shape = "record"; label = va ("state|think|step;"); break; case ex_bool: _print_expr (e->e.bool.e, level, id); if (e->e.bool.e->type == ex_block && e->e.bool.e->e.block.head) { expr_t *se; printf ("%*se_%p -> e_%p;\n", indent, "", e, e->e.bool.e); se = (expr_t *) e->e.bool.e->e.block.tail; if (se && se->type == ex_label && e->next) printf ("%*se_%p -> e_%p " "[constraint=false,style=dashed];\n", indent, "", se, e->next); } else { printf ("%*se_%p -> e_%p;\n", indent, "", e, e->e.bool.e); } label = ""; break; case ex_label: if (e->next) printf ("%*se_%p -> e_%p " "[constraint=false,style=dashed];\n", indent, "", e, e->next); label = e->e.label.name; break; case ex_block: { expr_t *se; label = ""; if (e->e.block.result) { _print_expr (e->e.block.result, level + 1, id); printf ("%*se_%p -> e_%p;\n", indent, "", e, e->e.block.result); } printf ("%*se_%p -> e_%p [style=dashed];\n", indent, "", e, e->e.block.head); printf ("%*ssubgraph cluster_%p {\n", indent, "", e); for (se = e->e.block.head; se; se = se->next) { _print_expr (se, level + 1, id); } for (se = e->e.block.head; se && se->next; se = se->next) { if ((se->type == ex_uexpr && se->e.expr.op == 'g') || se->type == ex_label || se->type == ex_bool) continue; printf ("%*se_%p -> e_%p " "[constraint=false,style=dashed];\n", indent, "", se, se->next); } if (se && se->type == ex_label && e->next) printf ("%*se_%p -> e_%p " "[constraint=false,style=dashed];\n", indent, "", se, e->next); printf ("%*s}\n", indent, ""); } break; case ex_expr: if (e->e.expr.op == 'c') { expr_t *p; int i; _print_expr (e->e.expr.e1, level, id); printf ("%*sp_%p [label=\"", indent, "", e); for (p = e->e.expr.e2, i = 0; p; p = p->next, i++) printf ("p%d%s", i, i, p->next ? "|" : ""); printf ("\",shape=record];\n"); for (p = e->e.expr.e2, i = 0; p; p = p->next, i++) { _print_expr (p, level + 1, id); printf ("%*sp_%p:p%d -> e_%p;\n", indent + 2, "", e, i, p); } printf ("%*se_%p -> e_%p;\n", indent, "", e, e->e.expr.e1); printf ("%*se_%p -> p_%p;\n", indent, "", e, e); } else if (e->e.expr.op == 'i' || e->e.expr.op == 'n' || e->e.expr.op == IFB || e->e.expr.op ==IFBE || e->e.expr.op == IFA || e->e.expr.op ==IFAE) { _print_expr (e->e.expr.e1, level, id); printf ("%*se_%p -> e_%p [label=\"t\"];\n", indent, "", e, e->e.expr.e1); printf ("%*se_%p -> e_%p [label=\"g\"];\n", indent, "", e, e->e.expr.e2); } else { _print_expr (e->e.expr.e1, level, id); _print_expr (e->e.expr.e2, level, id); printf ("%*se_%p -> e_%p [label=\"l\"];\n", indent, "", e, e->e.expr.e1); printf ("%*se_%p -> e_%p [label=\"r\"];\n", indent, "", e, e->e.expr.e2); } label = get_op_string (e->e.expr.op); break; case ex_uexpr: if (e->e.expr.op != 'g') _print_expr (e->e.expr.e1, level, id); printf ("%*se_%p -> e_%p;\n", indent, "", e, e->e.expr.e1); label = get_op_string (e->e.expr.op); break; case ex_symbol: label = e->e.symbol->name; break; case ex_temp: label = va ("tmp_%p", e); break; case ex_nil: label = "nil"; break; case ex_value: switch (e->e.value.type) { case ev_string: label = va ("\\\"%s\\\"", e->e.value.v.string_val); break; case ev_float: label = va ("%g", e->e.value.v.float_val); break; case ev_vector: label = va ("'%g %g %g'", e->e.value.v.vector_val[0], e->e.value.v.vector_val[1], e->e.value.v.vector_val[2]); break; case ev_quat: label = va ("'%g %g %g %g'", e->e.value.v.quaternion_val[0], e->e.value.v.quaternion_val[1], e->e.value.v.quaternion_val[2], e->e.value.v.quaternion_val[3]); break; case ev_pointer: label = va ("(%s)[%d]", pr_type_name[e->e.value.v.pointer.type->type], e->e.value.v.pointer.val); break; case ev_field: label = va ("field %d", e->e.value.v.pointer.val); break; case ev_entity: label = va ("ent %d", e->e.value.v.integer_val); break; case ev_func: label = va ("func %d", e->e.value.v.integer_val); break; case ev_integer: label = va ("%d", e->e.value.v.integer_val); break; case ev_short: label = va ("%d", e->e.value.v.short_val); break; case ev_void: case ev_invalid: case ev_type_count: internal_error (e, "weird expression type"); } } if (label) printf ("%*se_%p [label=\"%s\",shape=%s];\n", indent, "", e, label, shape); } void print_expr (expr_t *e) { static int id = 0; printf ("digraph expr_%p {\n", e); printf (" layout=dot; rankdir=TB; compound=true;\n"); _print_expr (e, 0, ++id); printf ("}\n"); } static expr_t * field_expr (expr_t *e1, expr_t *e2) { return type_mismatch (e1, e2, '.'); } expr_t * test_expr (expr_t *e, int test) { static float zero[4] = {0, 0, 0, 0}; expr_t *new = 0; etype_t type; if (e->type == ex_error) return e; if (!test) return unary_expr ('!', e); type = extract_type (e); if (e->type == ex_error) return e; switch (type) { case ev_type_count: internal_error (e, 0); case ev_void: if (options.traditional) { if (options.warnings.traditional) warning (e, "void has no value"); return e; } return error (e, "void has no value"); case ev_string: new = new_string_expr (0); break; // case ev_uinteger: case ev_integer: case ev_short: return e; case ev_float: if (options.code.fast_float || options.code.progsversion == PROG_ID_VERSION) return e; new = new_float_expr (0); break; case ev_vector: new = new_vector_expr (zero); break; case ev_entity: new = new_entity_expr (0); break; case ev_field: new = new_field_expr (0, 0, 0); break; case ev_func: new = new_func_expr (0); break; case ev_pointer: new = new_nil_expr (); break; case ev_quat: new = new_quaternion_expr (zero); break; case ev_invalid: return test_error (e, get_type (e)); } new->line = e->line; new->file = e->file; new = binary_expr (NE, e, new); new->line = e->line; new->file = e->file; return new; } void backpatch (ex_list_t *list, expr_t *label) { int i; expr_t *e; if (!list) return; for (i = 0; i < list->size; i++) { e = list->e[i]; if (e->type == ex_uexpr && e->e.expr.op == 'g') e->e.expr.e1 = label; else if (e->type == ex_expr && (e->e.expr.op == 'i' || e->e.expr.op == 'n')) e->e.expr.e2 = label; else { internal_error (e, 0); } } } static ex_list_t * merge (ex_list_t *l1, ex_list_t *l2) { ex_list_t *m; if (!l1 && !l2) { internal_error (0, 0); } if (!l2) return l1; if (!l1) return l2; m = malloc ((size_t)&((ex_list_t *)0)->e[l1->size + l2->size]); m->size = l1->size + l2->size; memcpy (m->e, l1->e, l1->size * sizeof (expr_t *)); memcpy (m->e + l1->size, l2->e, l2->size * sizeof (expr_t *)); return m; } static ex_list_t * make_list (expr_t *e) { ex_list_t *m; m = malloc ((size_t)&((ex_list_t *) 0)->e[1]); m->size = 1; m->e[0] = e; return m; } expr_t * convert_bool (expr_t *e, int block) { expr_t *b; if (e->type == ex_expr && (e->e.expr.op == '=' || e->e.expr.op == PAS) && !e->paren) { if (options.warnings.precedence) warning (e, "suggest parentheses around assignment " "used as truth value"); } if (e->type == ex_uexpr && e->e.expr.op == '!') { e = convert_bool (e->e.expr.e1, 0); if (e->type == ex_error) return e; e = unary_expr ('!', e); } if (e->type != ex_bool) { e = test_expr (e, 1); if (e->type == ex_error) return e; if (is_integer_val (e)) { b = new_unary_expr ('g', 0); if (expr_integer (e)) e = new_bool_expr (make_list (b), 0, b); else e = new_bool_expr (0, make_list (b), b); } else { b = new_block_expr (); append_expr (b, new_binary_expr ('i', e, 0)); append_expr (b, new_unary_expr ('g', 0)); e = new_bool_expr (make_list (b->e.block.head), make_list (b->e.block.head->next), b); } } if (block && e->e.bool.e->type != ex_block) { expr_t *block = new_block_expr (); append_expr (block, e->e.bool.e); e->e.bool.e = block; } return e; } static expr_t * convert_from_bool (expr_t *e, type_t *type) { expr_t *zero; expr_t *one; expr_t *cond; if (type == &type_float) { one = new_float_expr (1); zero = new_float_expr (0); } else if (type == &type_integer) { one = new_integer_expr (1); zero = new_integer_expr (0); // } else if (type == &type_uinteger) { // one = new_uinteger_expr (1); // zero = new_uinteger_expr (0); } else { return error (e, "can't convert from bool value"); } cond = new_expr (); *cond = *e; cond->next = 0; cond = conditional_expr (cond, one, zero); e->type = cond->type; e->e = cond->e; return e; } expr_t * bool_expr (int op, expr_t *label, expr_t *e1, expr_t *e2) { expr_t *block; if (!options.code.short_circuit) return binary_expr (op, e1, e2); e1 = convert_bool (e1, 0); if (e1->type == ex_error) return e1; e2 = convert_bool (e2, 0); if (e2->type == ex_error) return e2; block = new_block_expr (); append_expr (block, e1); append_expr (block, label); append_expr (block, e2); switch (op) { case OR: backpatch (e1->e.bool.false_list, label); return new_bool_expr (merge (e1->e.bool.true_list, e2->e.bool.true_list), e2->e.bool.false_list, block); break; case AND: backpatch (e1->e.bool.true_list, label); return new_bool_expr (e2->e.bool.true_list, merge (e1->e.bool.false_list, e2->e.bool.false_list), block); break; } internal_error (e1, 0); } void convert_int (expr_t *e) { e->e.value.v.float_val = expr_integer (e); e->e.value.type = ev_float; e->type = ex_value; } void convert_short (expr_t *e) { e->e.value.v.float_val = expr_short (e); e->e.value.type = ev_float; e->type = ex_value; } void convert_short_int (expr_t *e) { e->e.value.v.integer_val = expr_short (e); e->e.value.type = ev_integer; e->type = ex_value; } void convert_nil (expr_t *e, type_t *t) { memset (&e->e.value, 0, sizeof (e->e.value)); e->e.value.type = t->type; } int is_compare (int op) { if (op == EQ || op == NE || op == LE || op == GE || op == LT || op == GT || op == '>' || op == '<') return 1; return 0; } int is_math (int op) { if (op == '*' || op == '/' || op == '+' || op == '-') return 1; return 0; } int is_logic (int op) { if (op == OR || op == AND) return 1; 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 (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 (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 (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 (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 has_function_call (expr_t *e) { switch (e->type) { case ex_bool: return has_function_call (e->e.bool.e); case ex_block: if (e->e.block.is_call) return 1; for (e = e->e.block.head; e; e = e->next) if (has_function_call (e)) return 1; return 0; case ex_expr: if (e->e.expr.op == 'c') return 1; return (has_function_call (e->e.expr.e1) || has_function_call (e->e.expr.e2)); case ex_uexpr: if (e->e.expr.op != 'g') return has_function_call (e->e.expr.e1); default: return 0; } } expr_t * binary_expr (int op, expr_t *e1, expr_t *e2) { type_t *t1, *t2; type_t *type = 0; expr_t *e; if (e1->type == ex_error) return e1; if (e2->type == ex_error) return e2; if (e1->type == ex_block && e1->e.block.is_call && has_function_call (e2) && e1->e.block.result) { e = new_temp_def_expr (get_type (e1->e.block.result)); inc_users (e); // for the block itself e1 = assign_expr (e, e1); } if (op == '.') return field_expr (e1, e2); if (op == OR || op == AND) { e1 = test_expr (e1, true); e2 = test_expr (e2, true); } if (e1->type == ex_error) return e1; if (e2->type == ex_error) return e2; t1 = get_type (e1); t2 = get_type (e2); if (!t1 || !t2) { internal_error (e1, 0); } 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 (e1->type == ex_bool) e1 = convert_from_bool (e1, t2); if (e2->type == ex_bool) e2 = convert_from_bool (e2, t1); if ((e = check_precedence (op, e1, e2))) return e; type = t1; if (is_compare (op) || is_logic (op)) { if (options.code.progsversion > PROG_ID_VERSION) type = &type_integer; else type = &type_float; } else if (op == '*' && t1 == &type_vector && t2 == &type_vector) { type = &type_float; } if (!type) internal_error (e1, 0); if (options.code.progsversion == PROG_ID_VERSION) { switch (op) { case '%': { expr_t *tmp1, *tmp2, *tmp3, *t1, *t2; e = new_block_expr (); t1 = new_temp_def_expr (&type_float); t2 = new_temp_def_expr (&type_float); tmp1 = new_temp_def_expr (&type_float); tmp2 = new_temp_def_expr (&type_float); tmp3 = new_temp_def_expr (&type_float); append_expr (e, new_bind_expr (e1, t1)); e1 = binary_expr ('&', t1, t1); append_expr (e, new_bind_expr (e1, tmp1)); append_expr (e, new_bind_expr (e2, t2)); e2 = binary_expr ('&', t2, t2); append_expr (e, new_bind_expr (e2, tmp2)); e1 = binary_expr ('/', tmp1, tmp2); append_expr (e, assign_expr (tmp3, e1)); e2 = binary_expr ('&', tmp3, tmp3); append_expr (e, new_bind_expr (e2, tmp3)); e1 = binary_expr ('*', tmp2, tmp3); e2 = binary_expr ('-', tmp1, e1); e->e.block.result = e2; return e; } break; } } e = new_binary_expr (op, e1, e2); e->e.expr.type = type; return e; } expr_t * asx_expr (int op, expr_t *e1, expr_t *e2) { if (e1->type == ex_error) return e1; else if (e2->type == ex_error) return e2; else { expr_t *e = new_expr (); *e = *e1; e2->paren = 1; return assign_expr (e, binary_expr (op, e1, e2)); } } expr_t * unary_expr (int op, expr_t *e) { vec3_t v; quat_t q; const char *s; if (e->type == ex_error) return e; switch (op) { case '-': if (is_constant (e)) { switch (extract_type (e)) { case ev_string: case ev_entity: case ev_field: case ev_func: case ev_pointer: return error (e, "invalid type for unary -"); case ev_float: return new_float_expr (-expr_float (e)); case ev_vector: VectorNegate (expr_vector (e), v); return new_vector_expr (v); case ev_quat: QuatNegate (expr_vector (e), q); return new_vector_expr (q); case ev_integer: return new_integer_expr (-expr_integer (e)); case ev_short: return new_short_expr (-expr_short (e)); case ev_invalid: case ev_type_count: case ev_void: break; } internal_error (e, "weird expression type"); } switch (e->type) { case ex_value: // should be handled above case ex_error: case ex_label: case ex_state: internal_error (e, 0); case ex_uexpr: if (e->e.expr.op == '-') return e->e.expr.e1; case ex_block: if (!e->e.block.result) return error (e, "invalid type for unary -"); case ex_expr: case ex_bool: case ex_temp: { expr_t *n = new_unary_expr (op, e); n->e.expr.type = e->e.expr.type; return n; } case ex_symbol: { expr_t *n = new_unary_expr (op, e); n->e.expr.type = e->e.symbol->type; return n; } case ex_nil: return error (e, "invalid type for unary -"); } break; case '!': if (is_constant (e)) { switch (extract_type (e)) { case ev_entity: case ev_field: case ev_func: case ev_pointer: internal_error (e, 0); case ev_string: s = expr_string (e); return new_integer_expr (!s || !s[0]); case ev_float: return new_integer_expr (!expr_float (e)); case ev_vector: return new_integer_expr (!VectorIsZero (expr_vector (e))); case ev_quat: return new_integer_expr (!QuatIsZero (expr_quaternion (e))); case ev_integer: return new_integer_expr (!expr_integer (e)); case ev_short: return new_short_expr (!expr_short (e)); case ev_invalid: case ev_type_count: case ev_void: break; } internal_error (e, "weird expression type"); } switch (e->type) { case ex_value: // should be handled above case ex_error: case ex_label: case ex_state: internal_error (e, 0); case ex_bool: return new_bool_expr (e->e.bool.false_list, e->e.bool.true_list, e); case ex_block: if (!e->e.block.result) return error (e, "invalid type for unary !"); case ex_uexpr: case ex_expr: case ex_symbol: case ex_temp: { expr_t *n = new_unary_expr (op, e); if (options.code.progsversion > PROG_ID_VERSION) n->e.expr.type = &type_integer; else n->e.expr.type = &type_float; return n; } case ex_nil: return error (e, "invalid type for unary !"); } break; case '~': if (is_constant (e)) { switch (extract_type (e)) { case ev_string: case ev_entity: case ev_field: case ev_func: case ev_pointer: case ev_vector: return error (e, "invalid type for unary -"); case ev_float: return new_float_expr (~(int) expr_float (e)); case ev_quat: QuatConj (expr_vector (e), q); return new_vector_expr (q); case ev_integer: return new_integer_expr (~expr_integer (e)); case ev_short: return new_short_expr (~expr_short (e)); case ev_invalid: case ev_type_count: case ev_void: break; } internal_error (e, "weird expression type"); } switch (e->type) { case ex_value: // should be handled above case ex_error: case ex_label: case ex_state: internal_error (e, 0); case ex_uexpr: if (e->e.expr.op == '~') return e->e.expr.e1; goto bitnot_expr; case ex_block: if (!e->e.block.result) return error (e, "invalid type for unary ~"); goto bitnot_expr; case ex_expr: case ex_bool: case ex_symbol: case ex_temp: bitnot_expr: if (options.code.progsversion == PROG_ID_VERSION) { expr_t *n1 = new_integer_expr (-1); return binary_expr ('-', n1, e); } else { expr_t *n = new_unary_expr (op, e); type_t *t = get_type (e); if (t != &type_integer && t != &type_float && t != &type_quaternion) return error (e, "invalid type for unary ~"); n->e.expr.type = t; return n; } case ex_nil: return error (e, "invalid type for unary ~"); } break; case '.': if (extract_type (e) != ev_pointer) return error (e, "invalid type for unary ."); e = new_unary_expr ('.', e); e->e.expr.type = get_type (e->e.expr.e1)->t.fldptr.type; return e; case '+': return e; // FIXME typechecking } internal_error (e, 0); } expr_t * build_function_call (expr_t *fexpr, type_t *ftype, expr_t *params) { expr_t *e; int arg_count = 0, parm_count = 0; int i; expr_t *args = 0, **a = &args; type_t *arg_types[MAX_PARMS]; expr_t *arg_exprs[MAX_PARMS][2]; int arg_expr_count = 0; expr_t *call; expr_t *err = 0; for (e = params; e; e = e->next) { if (e->type == ex_error) return e; arg_count++; } if (arg_count > MAX_PARMS) { return error (fexpr, "more than %d parameters", MAX_PARMS); } if (ftype->t.func.num_params < -1) { if (-arg_count > ftype->t.func.num_params + 1) { if (!options.traditional) return error (fexpr, "too few arguments"); if (options.warnings.traditional) warning (fexpr, "too few arguments"); } parm_count = -ftype->t.func.num_params - 1; } else if (ftype->t.func.num_params >= 0) { if (arg_count > ftype->t.func.num_params) { return error (fexpr, "too many arguments"); } else if (arg_count < ftype->t.func.num_params) { if (!options.traditional) return error (fexpr, "too few arguments"); if (options.warnings.traditional) warning (fexpr, "too few arguments"); } parm_count = ftype->t.func.num_params; } for (i = arg_count - 1, e = params; i >= 0; i--, e = e->next) { type_t *t = get_type (e); if (!type_size (t)) err = error (e, "type of formal parameter %d is incomplete", i + 1); if (type_size (t) > type_size (&type_param)) err = error (e, "formal parameter %d is too large to be passed by" " value", i + 1); if (ftype->t.func.param_types[i] == &type_float && is_integer_val (e)) { convert_int (e); t = &type_float; } if (i < parm_count) { if (e->type == ex_nil) convert_nil (e, t = ftype->t.func.param_types[i]); if (e->type == ex_bool) convert_from_bool (e, ftype->t.func.param_types[i]); if (e->type == ex_error) return e; if (!type_assignable (ftype->t.func.param_types[i], t)) { err = param_mismatch (e, i + 1, fexpr->e.symbol->name, ftype->t.func.param_types[i], t); } t = ftype->t.func.param_types[i]; } else { if (e->type == ex_nil) convert_nil (e, t = &type_vector); //FIXME largest param size if (e->type == ex_bool) convert_from_bool (e, get_type (e)); if (is_integer_val (e) && options.code.progsversion == PROG_ID_VERSION) convert_int (e); if (is_integer_val (e) && options.warnings.vararg_integer) warning (e, "passing integer constant into ... function"); } arg_types[arg_count - 1 - i] = t; } if (err) return err; call = new_block_expr (); call->e.block.is_call = 1; for (e = params, i = 0; e; e = e->next, i++) { if (has_function_call (e)) { *a = new_temp_def_expr (arg_types[i]); arg_exprs[arg_expr_count][0] = cast_expr (arg_types[i], e); arg_exprs[arg_expr_count][1] = *a; arg_expr_count++; } else { *a = cast_expr (arg_types[i], e); } // new_binary_expr calls inc_users for both args, but inc_users doesn't // walk expression chains so only the first arg expression in the chain // (the last arg in the call) gets its user count incremented, thus // ensure all other arg expressions get their user counts incremented. if (a != &args) inc_users (*a); a = &(*a)->next; } for (i = 0; i < arg_expr_count - 1; i++) { append_expr (call, assign_expr (arg_exprs[i][1], arg_exprs[i][0])); } if (arg_expr_count) { e = new_bind_expr (arg_exprs[arg_expr_count - 1][0], arg_exprs[arg_expr_count - 1][1]); inc_users (arg_exprs[arg_expr_count - 1][0]); inc_users (arg_exprs[arg_expr_count - 1][1]); append_expr (call, e); } e = new_binary_expr ('c', fexpr, args); e->e.expr.type = ftype->t.func.type; append_expr (call, e); if (ftype->t.func.type != &type_void) { call->e.block.result = new_ret_expr (ftype->t.func.type); } else if (options.traditional) { call->e.block.result = new_ret_expr (&type_float); } return call; } expr_t * function_expr (expr_t *fexpr, expr_t *params) { type_t *ftype; find_function (fexpr, params); ftype = get_type (fexpr); if (fexpr->type == ex_error) return fexpr; if (ftype->type != ev_func) { if (fexpr->type == ex_symbol) return error (fexpr, "Called object \"%s\" is not a function", fexpr->e.symbol->name); else return error (fexpr, "Called object is not a function"); } if (fexpr->type == ex_symbol && params && is_string_val (params)) { // FIXME eww, I hate this, but it's needed :( // FIXME make a qc hook? :) if (strncmp (fexpr->e.symbol->name, "precache_sound", 14) == 0) PrecacheSound (expr_string (params), fexpr->e.symbol->name[14]); else if (strncmp (fexpr->e.symbol->name, "precache_model", 14) == 0) PrecacheModel (expr_string (params), fexpr->e.symbol->name[14]); else if (strncmp (fexpr->e.symbol->name, "precache_file", 13) == 0) PrecacheFile (expr_string (params), fexpr->e.symbol->name[13]); } return build_function_call (fexpr, ftype, params); } expr_t * return_expr (function_t *f, expr_t *e) { type_t *t; if (!e) { if (f->sym->type->t.func.type != &type_void) { if (options.traditional) { if (options.warnings.traditional) warning (e, "return from non-void function without a value"); e = new_nil_expr (); } else { e = error (e, "return from non-void function without a value"); return e; } } return new_unary_expr ('r', 0); } t = get_type (e); if (e->type == ex_error) return e; if (f->sym->type->t.func.type == &type_void) { if (!options.traditional) return error (e, "returning a value for a void function"); if (options.warnings.traditional) warning (e, "returning a value for a void function"); } if (e->type == ex_bool) e = convert_from_bool (e, f->sym->type->t.func.type); if (f->sym->type->t.func.type == &type_float && is_integer_val (e)) { convert_int (e); t = &type_float; } if (t == &type_void) { if (e->type == ex_nil) { t = f->sym->type->t.func.type; convert_nil (e, t); if (e->type == ex_nil) return error (e, "invalid return type for NIL"); } else { if (!options.traditional) return error (e, "void value not ignored as it ought to be"); if (options.warnings.traditional) warning (e, "void value not ignored as it ought to be"); //FIXME does anything need to be done here? } } if (!type_assignable (f->sym->type->t.func.type, t)) { if (!options.traditional) return error (e, "type mismatch for return value of %s", f->sym->name); if (options.warnings.traditional) warning (e, "type mismatch for return value of %s", f->sym->name); } else { if (f->sym->type->t.func.type != t) e = cast_expr (f->sym->type->t.func.type, e); } return new_unary_expr ('r', e); } expr_t * conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2) { expr_t *block = new_block_expr (); type_t *type1 = get_type (e1); type_t *type2 = get_type (e2); expr_t *tlabel = new_label_expr (); expr_t *flabel = new_label_expr (); expr_t *elabel = new_label_expr (); if (cond->type == ex_error) return cond; if (e1->type == ex_error) return e1; if (e2->type == ex_error) return e2; cond = convert_bool (cond, 1); if (cond->type == ex_error) return cond; backpatch (cond->e.bool.true_list, tlabel); backpatch (cond->e.bool.false_list, flabel); block->e.block.result = (type1 == type2) ? new_temp_def_expr (type1) : 0; append_expr (block, cond); append_expr (cond->e.bool.e, flabel); if (block->e.block.result) append_expr (block, assign_expr (block->e.block.result, e2)); else append_expr (block, e2); append_expr (block, new_unary_expr ('g', elabel)); append_expr (block, tlabel); if (block->e.block.result) append_expr (block, assign_expr (block->e.block.result, e1)); else append_expr (block, e1); append_expr (block, elabel); return block; } expr_t * incop_expr (int op, expr_t *e, int postop) { expr_t *one; if (e->type == ex_error) return e; one = new_integer_expr (1); // integer constants get auto-cast to float if (postop) { expr_t *t1, *t2; type_t *type = get_type (e); expr_t *block = new_block_expr (); expr_t *res = new_expr (); t1 = new_temp_def_expr (type); t2 = new_temp_def_expr (type); append_expr (block, assign_expr (t1, e)); append_expr (block, assign_expr (t2, binary_expr (op, t1, one))); res = copy_expr (e); if (res->type == ex_uexpr && res->e.expr.op == '.') res = pointer_expr (address_expr (res, 0, 0)); append_expr (block, assign_expr (res, t2)); block->e.block.result = t1; return block; } else { return asx_expr (op, e, one); } } expr_t * array_expr (expr_t *array, expr_t *index) { type_t *array_type = get_type (array); type_t *index_type = get_type (index); expr_t *scale; expr_t *e; int ind = 0; if (array->type == ex_error) return array; if (index->type == ex_error) return index; if (array_type->type != ev_pointer && !is_array (array_type)) return error (array, "not an array"); if (index_type != &type_integer) return error (index, "invalid array index type"); if (is_short_val (index)) ind = expr_short (index); if (is_integer_val (index)) ind = expr_integer (index); if (array_type->t.func.num_params && is_constant (index) && (ind < array_type->t.array.base || ind - array_type->t.array.base >= array_type->t.array.size)) return error (index, "array index out of bounds"); scale = new_integer_expr (type_size (array_type->t.array.type)); index = binary_expr ('*', index, scale); index = binary_expr ('-', index, binary_expr ('*', new_integer_expr (array_type->t.array.base), scale)); index = fold_constants (index); if (is_short_val (index)) ind = expr_short (index); if (is_integer_val (index)) ind = expr_integer (index); if ((is_constant (index) && ind < 32768 && ind >= -32768)) index = new_short_expr (ind); if (is_array (array_type)) { e = address_expr (array, index, array_type->t.array.type); } else { if (!is_short_val (index) || expr_short (index)) { e = new_binary_expr ('&', array, index); //e->e.expr.type = array_type->aux_type; e->e.expr.type = array_type; } else { e = array; } } e = unary_expr ('.', e); return e; } expr_t * pointer_expr (expr_t *pointer) { type_t *pointer_type = get_type (pointer); if (pointer->type == ex_error) return pointer; if (pointer_type->type != ev_pointer) return error (pointer, "not a pointer"); return array_expr (pointer, new_integer_expr (0)); } expr_t * address_expr (expr_t *e1, expr_t *e2, type_t *t) { expr_t *e; if (e1->type == ex_error) return e1; if (!t) t = get_type (e1); switch (e1->type) { case ex_symbol: if (e1->e.symbol->sy_type == sy_var) { e = new_unary_expr ('&', e1); e->e.expr.type = pointer_type (t); break; } return error (e1, "invalid type for unary &"); case ex_expr: if (e1->e.expr.op == '.') { e = e1; e->e.expr.op = '&'; e->e.expr.type = pointer_type (e->e.expr.type); break; } else if (e1->e.expr.op == 'b') { e = new_unary_expr ('&', e1); e->e.expr.type = pointer_type (e1->e.expr.type); break; } return error (e1, "invalid type for unary &"); case ex_uexpr: if (e1->e.expr.op == '.') { e = e1->e.expr.e1; if (e->type == ex_expr && e->e.expr.op == '.') { e->e.expr.type = pointer_type (e->e.expr.type); e->e.expr.op = '&'; } break; } return error (e1, "invalid type for unary &"); default: return error (e1, "invalid type for unary &"); } if (e2) { if (e2->type == ex_error) return e2; if (e->type == ex_value && e->e.value.type == ev_pointer && is_short_val (e2)) { e->e.value.v.pointer.val += expr_short (e2); e->e.value.v.pointer.type = t; } else { if (!is_short_val (e2) || expr_short (e2)) { if (e->type == ex_expr && e->e.expr.op == '&') { e = new_binary_expr ('&', e->e.expr.e1, binary_expr ('+', e->e.expr.e2, e2)); } else { e = new_binary_expr ('&', e, e2); } } if (e->type == ex_expr || e->type == ex_uexpr) e->e.expr.type = pointer_type (t); } } return e; } expr_t * build_if_statement (expr_t *test, expr_t *s1, expr_t *s2) { int line = pr.source_line; string_t file = pr.source_file; expr_t *if_expr; expr_t *tl = new_label_expr (); expr_t *fl = new_label_expr (); pr.source_line = test->line; pr.source_file = test->file; if_expr = new_block_expr (); test = convert_bool (test, 1); if (test->type != ex_error) { backpatch (test->e.bool.true_list, tl); backpatch (test->e.bool.false_list, fl); append_expr (test->e.bool.e, tl); append_expr (if_expr, test); } append_expr (if_expr, s1); if (s2) { expr_t *nl = new_label_expr (); append_expr (if_expr, new_unary_expr ('g', nl)); append_expr (if_expr, fl); append_expr (if_expr, s2); append_expr (if_expr, nl); } else { append_expr (if_expr, fl); } pr.source_line = line; pr.source_file = file; return if_expr; } expr_t * build_while_statement (expr_t *test, expr_t *statement, expr_t *break_label, expr_t *continue_label) { int line = pr.source_line; string_t file = pr.source_file; expr_t *l1 = new_label_expr (); expr_t *l2 = break_label; expr_t *while_expr; pr.source_line = test->line; pr.source_file = test->file; while_expr = new_block_expr (); append_expr (while_expr, new_unary_expr ('g', continue_label)); append_expr (while_expr, l1); append_expr (while_expr, statement); append_expr (while_expr, continue_label); test = convert_bool (test, 1); if (test->type != ex_error) { backpatch (test->e.bool.true_list, l1); backpatch (test->e.bool.false_list, l2); append_expr (test->e.bool.e, l2); append_expr (while_expr, test); } pr.source_line = line; pr.source_file = file; return while_expr; } expr_t * build_do_while_statement (expr_t *statement, expr_t *test, expr_t *break_label, expr_t *continue_label) { expr_t *l1 = new_label_expr (); int line = pr.source_line; string_t file = pr.source_file; expr_t *do_while_expr; pr.source_line = test->line; pr.source_file = test->file; do_while_expr = new_block_expr (); append_expr (do_while_expr, l1); append_expr (do_while_expr, statement); append_expr (do_while_expr, continue_label); test = convert_bool (test, 1); if (test->type != ex_error) { backpatch (test->e.bool.true_list, l1); backpatch (test->e.bool.false_list, break_label); append_expr (test->e.bool.e, break_label); append_expr (do_while_expr, test); } pr.source_line = line; pr.source_file = file; return do_while_expr; } expr_t * build_for_statement (expr_t *init, expr_t *test, expr_t *next, expr_t *statement, expr_t *break_label, expr_t *continue_label) { expr_t *tl = new_label_expr (); expr_t *fl = break_label; expr_t *l1 = 0; expr_t *t; int line = pr.source_line; string_t file = pr.source_file; expr_t *for_expr; if (next) t = next; else if (test) t = test; else if (init) t = init; else t = continue_label; pr.source_line = t->line; pr.source_file = t->file; for_expr = new_block_expr (); append_expr (for_expr, init); if (test) { l1 = new_label_expr (); append_expr (for_expr, new_unary_expr ('g', l1)); } append_expr (for_expr, tl); append_expr (for_expr, statement); append_expr (for_expr, continue_label); append_expr (for_expr, next); if (test) { append_expr (for_expr, l1); test = convert_bool (test, 1); if (test->type != ex_error) { backpatch (test->e.bool.true_list, tl); backpatch (test->e.bool.false_list, fl); append_expr (test->e.bool.e, fl); append_expr (for_expr, test); } } else { append_expr (for_expr, new_unary_expr ('g', tl)); append_expr (for_expr, fl); } pr.source_line = line; pr.source_file = file; return for_expr; } expr_t * build_state_expr (expr_t *frame, expr_t *think, expr_t *step) { if (is_integer_val (frame)) convert_int (frame); if (!type_assignable (&type_float, get_type (frame))) return error (frame, "invalid type for frame number"); if (extract_type (think) != ev_func) return error (think, "invalid type for think"); if (step) { if (is_integer_val (step)) convert_int (step); if (!type_assignable (&type_float, get_type (step))) return error (step, "invalid type for frame number"); } return new_state_expr (frame, think, step); } static int is_indirect (expr_t *e) { if (e->type == ex_expr && e->e.expr.op == '.') return 1; if (!(e->type == ex_uexpr && e->e.expr.op == '.')) return 0; e = e->e.expr.e1; if ((e->type != ex_value && e->e.value.type != ev_pointer) || !(POINTER_VAL (e->e.value.v.pointer) >= 0 && POINTER_VAL (e->e.value.v.pointer) < 65536)) { return 1; } return 0; } static inline int is_lvalue (expr_t *e) { if (e->type == ex_symbol) { switch (e->e.symbol->sy_type) { case sy_var: return 1; case sy_const: return 0; case sy_type: return 0; case sy_expr: return 0; case sy_func: return 0; } } if (e->type == ex_temp) return 1; if (e->type == ex_expr && e->e.expr.op == '.') return 1; if (e->type == ex_uexpr && e->e.expr.op == '.') return 1; return 0; } expr_t * assign_expr (expr_t *e1, expr_t *e2) { int op = '='; type_t *t1, *t2, *type; expr_t *e; if (e1->type == ex_error) return e1; if (e2->type == ex_error) return e2; if (options.traditional) { if (e2->type == ex_expr && !e2->paren && (e2->e.expr.op == AND || e2->e.expr.op == OR)) { notice (e2, "precedence of `%s' and `%s' inverted for " "traditional code", get_op_string (op), get_op_string (e2->e.expr.op)); e1 = assign_expr (e1, e2->e.expr.e1); e1->paren = 1; return binary_expr (e2->e.expr.op, e1, e2->e.expr.e2); } } if (!is_lvalue (e1)) { if (options.traditional) warning (e1, "invalid lvalue in assignment"); else return error (e1, "invalid lvalue in assignment"); } t1 = get_type (e1); t2 = get_type (e2); if (!t1 || !t2) { internal_error (e1, 0); } //XXX func = func ??? if (t1->type != ev_pointer || !is_array (t2)) ;//FIXME check_initialized (e2); else { e2 = address_expr (e2, 0, t2->t.fldptr.type); // FIXME t2 = get_type (e2); } if (e2->type == ex_bool) e2 = convert_from_bool (e2, t1); if (t1->type != ev_void && e2->type == ex_nil) { t2 = t1; convert_nil (e2, t2); } e2->rvalue = 1; if (!type_assignable (t1, t2)) { if (options.traditional) { if (t1->type == ev_func && t2->type == ev_func) { warning (e1, "assignment between disparate function types"); } else if (t1->type == ev_float && t2->type == ev_vector) { warning (e1, "assignment of vector to float"); e2 = binary_expr ('.', e2, new_name_expr ("x")); } else if (t1->type == ev_vector && t2->type == ev_float) { warning (e1, "assignment of float to vector"); e1 = binary_expr ('.', e1, new_name_expr ("x")); } else { return type_mismatch (e1, e2, op); } } else { return type_mismatch (e1, e2, op); } } type = t1; if (is_indirect (e1) && is_indirect (e2)) { if (is_struct (get_type (e2))) { e1 = address_expr (e1, 0, 0); e2 = address_expr (e2, 0, 0); e = new_move_expr (e1, e2, get_type (e2)); } else { expr_t *temp = new_temp_def_expr (t1); e = new_block_expr (); append_expr (e, assign_expr (temp, e2)); append_expr (e, assign_expr (e1, temp)); e->e.block.result = temp; } return e; } else if (is_indirect (e1)) { if (is_struct (get_type (e1))) { e1 = address_expr (e1, 0, 0); return new_move_expr (e1, e2, get_type (e2)); } if (e1->type == ex_expr) { if (get_type (e1->e.expr.e1) == &type_entity) { type = e1->e.expr.type; e1->e.expr.type = pointer_type (type); e1->e.expr.op = '&'; } op = PAS; } else { e = e1->e.expr.e1; if ((e->type != ex_value || e->e.value.type != ev_pointer) || !(POINTER_VAL (e->e.value.v.pointer) > 0 && POINTER_VAL (e->e.value.v.pointer) < 65536)) { e1 = e; op = PAS; } } } else if (is_indirect (e2)) { if (is_struct (get_type (e1))) { e2 = address_expr (e2, 0, 0); e2->rvalue = 1; return new_move_expr (e1, e2, get_type (e2)); } if (e2->type == ex_uexpr) { e = e2->e.expr.e1; if ((e->type != ex_value || e->e.value.type != ev_pointer) || !(POINTER_VAL (e->e.value.v.pointer) > 0 && POINTER_VAL (e->e.value.v.pointer) < 65536)) { if (e->type == ex_expr && e->e.expr.op == '&' && e->e.expr.type->type == ev_pointer && !is_constant (e)) { e2 = e; e2->e.expr.op = '.'; e2->e.expr.type = t2; e2->rvalue = 1; } } } } if (is_struct (get_type (e1))) { return new_move_expr (e1, e2, get_type (e2)); } if (!type) internal_error (e1, 0); e = new_binary_expr (op, e1, e2); e->e.expr.type = type; return e; } expr_t * cast_expr (type_t *type, expr_t *e) { expr_t *c; type_t *e_type; if (e->type == ex_error) return e; e_type = get_type (e); if (type == e_type) return e; if (!(type->type == ev_pointer && (e_type->type == ev_pointer || e_type == &type_integer //|| e_type == &type_uinteger || is_array (e_type))) && !(type->type == ev_func && e_type->type == ev_func) && !(((type == &type_integer) && (e_type == &type_float || e_type == &type_integer || e_type->type == ev_pointer)) || (type == &type_float && (e_type == &type_integer)))) { return cast_error (e, get_type (e), type); } if (is_array (e_type)) { return address_expr (e, 0, 0); } if (e->type == ex_uexpr && e->e.expr.op == '.') { e->e.expr.type = type; c = e; } else { c = new_unary_expr ('C', e); c->e.expr.type = type; } return c; } void init_elements (def_t *def, expr_t *eles) { } expr_t * selector_expr (keywordarg_t *selector) { dstring_t *sel_id = dstring_newstr (); expr_t *sel; def_t *sel_def; int index; selector = copy_keywordargs (selector); selector = (keywordarg_t *) reverse_params ((param_t *) selector); selector_name (sel_id, selector); index = selector_index (sel_id->str); index *= type_size (type_SEL.t.fldptr.type); sel_def = get_def (type_SEL.t.fldptr.type, "_OBJ_SELECTOR_TABLE", pr.scope, st_extern); sel = 0; //FIXME new_def_expr (sel_def); dstring_delete (sel_id); return address_expr (sel, new_short_expr (index), 0); } expr_t * protocol_expr (const char *protocol) { return error (0, "not implemented"); } expr_t * encode_expr (type_t *type) { dstring_t *encoding = dstring_newstr (); expr_t *e; encode_type (encoding, type); e = new_string_expr (encoding->str); free (encoding); return e; } expr_t * super_expr (class_type_t *class_type) { def_t *super_d; expr_t *super; expr_t *e; expr_t *super_block; class_t *class; class_type_t _class_type; if (!class_type) return error (0, "`super' used outside of class implementation"); class = extract_class (class_type); if (!class->super_class) return error (0, "%s has no super class", class->name); super_d = 0;//FIXME get_def (&type_Super, ".super", current_func->scope, st_local); def_initialized (super_d); super = 0; //FIXME new_def_expr (super_d); super_block = new_block_expr (); e = assign_expr (binary_expr ('.', super, new_name_expr ("self")), new_name_expr ("self")); append_expr (super_block, e); _class_type.type = ct_class; _class_type.c.class = class; e = 0; //FIXME new_def_expr (class_def (&_class_type, 1)); e = assign_expr (binary_expr ('.', super, new_name_expr ("class")), binary_expr ('.', e, new_name_expr ("super_class"))); append_expr (super_block, e); e = address_expr (super, 0, &type_void); super_block->e.block.result = e; return super_block; } expr_t * message_expr (expr_t *receiver, keywordarg_t *message) { expr_t *args = 0, **a = &args; expr_t *selector = selector_expr (message); expr_t *call; keywordarg_t *m; int self = 0, super = 0, class_msg = 0; type_t *rec_type; class_t *class; method_t *method; if (receiver->type == ex_symbol && strcmp (receiver->e.symbol->name, "super") == 0) { super = 1; receiver = super_expr (current_class); if (receiver->type == ex_error) return receiver; class = extract_class (current_class); rec_type = class->type; } else { if (receiver->type == ex_symbol) { if (strcmp (receiver->e.symbol->name, "self") == 0) self = 1; if (get_class (receiver->e.symbol, 0)) class_msg = 1; } rec_type = get_type (receiver); if (receiver->type == ex_error) return receiver; if (rec_type->type != ev_pointer || !is_class (rec_type->t.fldptr.type)) return error (receiver, "not a class/object"); if (self) { class = extract_class (current_class); if (rec_type == &type_Class) class_msg = 1; } else { class = rec_type->t.fldptr.type->t.class; } } method = class_message_response (class, class_msg, selector); if (method) rec_type = method->type->t.func.type; for (m = message; m; m = m->next) { *a = m->expr; while ((*a)) a = &(*a)->next; } *a = selector; a = &(*a)->next; *a = receiver; if (method) { expr_t *err; if ((err = method_check_params (method, args))) return err; call = build_function_call (send_message (super), method->type, args); } else { call = build_function_call (send_message (super), &type_IMP, args); } if (call->type == ex_error) return receiver; call->e.block.result = new_ret_expr (rec_type); return call; } expr_t * sizeof_expr (expr_t *expr, struct type_s *type) { if (!((!expr) ^ (!type))) { internal_error (0, 0); } if (!type) type = get_type (expr); expr = new_integer_expr (type_size (type)); return expr; } static void report_function (expr_t *e) { static function_t *last_func = (function_t *)-1L; static string_t last_file; string_t file = pr.source_file; srcline_t *srcline; if (e) file = e->file; if (file != last_file) { for (srcline = pr.srcline_stack; srcline; srcline = srcline->next) fprintf (stderr, "In file included from %s:%d:\n", G_GETSTR (srcline->source_file), srcline->source_line); } last_file = file; if (current_func != last_func) { if (current_func) { fprintf (stderr, "%s: In function `%s':\n", G_GETSTR (file), current_func->name); } else if (current_class) { fprintf (stderr, "%s: In class `%s':\n", G_GETSTR (file), get_class_name (current_class, 1)); } else { fprintf (stderr, "%s: At top level:\n", G_GETSTR (file)); } } last_func = current_func; } static void _warning (expr_t *e, const char *fmt, va_list args) { string_t file = pr.source_file; int line = pr.source_line; report_function (e); if (options.warnings.promote) { options.warnings.promote = 0; // want to do this only once fprintf (stderr, "%s: warnings treated as errors\n", "qfcc"); pr.error_count++; } if (e) { file = e->file; line = e->line; } fprintf (stderr, "%s:%d: warning: ", G_GETSTR (file), line); vfprintf (stderr, fmt, args); fputs ("\n", stderr); } expr_t * notice (expr_t *e, const char *fmt, ...) { va_list args; if (options.notices.silent) return e; va_start (args, fmt); if (options.notices.promote) { _warning (e, fmt, args); } else { string_t file = pr.source_file; int line = pr.source_line; report_function (e); if (e) { file = e->file; line = e->line; } fprintf (stderr, "%s:%d: notice: ", G_GETSTR (file), line); vfprintf (stderr, fmt, args); fputs ("\n", stderr); } va_end (args); return e; } expr_t * warning (expr_t *e, const char *fmt, ...) { va_list args; va_start (args, fmt); _warning (e, fmt, args); va_end (args); return e; } static void _error (expr_t *e, const char *err, const char *fmt, va_list args) { string_t file = pr.source_file; int line = pr.source_line; report_function (e); if (e) { file = e->file; line = e->line; } fprintf (stderr, "%s:%d: %s%s", G_GETSTR (file), line, err, fmt ? ": " : ""); if (fmt) vfprintf (stderr, fmt, args); fputs ("\n", stderr); pr.error_count++; } void internal_error (expr_t *e, const char *fmt, ...) { va_list args; va_start (args, fmt); _error (e, "internal error", fmt, args); va_end (args); abort (); } expr_t * error (expr_t *e, const char *fmt, ...) { va_list args; va_start (args, fmt); _error (e, "error", fmt, args); va_end (args); if (!e) e = new_expr (); e->type = ex_error; return e; }