mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-24 05:11:42 +00:00
10ad8bebbd
useful. Also, fix a bug in PrecacheModel where it was checking MAX_SOUNDS and numsounds instead of models.
1184 lines
23 KiB
C
1184 lines
23 KiB
C
#include <stdlib.h>
|
|
|
|
#include <QF/mathlib.h>
|
|
#include <QF/va.h>
|
|
|
|
#include "qfcc.h"
|
|
#include "scope.h"
|
|
#include "qc-parse.h"
|
|
|
|
extern function_t *current_func;
|
|
|
|
int lineno_base;
|
|
|
|
static etype_t qc_types[] = {
|
|
ev_void, // ex_label
|
|
ev_void, // ex_block
|
|
ev_void, // ex_expr
|
|
ev_void, // ex_uexpr
|
|
ev_void, // ex_def
|
|
|
|
ev_string, // ex_string
|
|
ev_float, // ex_float
|
|
ev_vector, // ex_vector
|
|
ev_entity, // ex_entity
|
|
ev_field, // ex_field
|
|
ev_func, // ex_func
|
|
ev_pointer, // ex_pointer
|
|
ev_quaternion, // ex_quaternion
|
|
ev_int, // ex_int
|
|
};
|
|
|
|
static type_t *types[] = {
|
|
&type_void,
|
|
&type_string,
|
|
&type_float,
|
|
&type_vector,
|
|
&type_entity,
|
|
&type_field,
|
|
&type_function,
|
|
&type_pointer,
|
|
&type_quaternion,
|
|
//XXX&type_int,
|
|
};
|
|
|
|
static expr_type expr_types[] = {
|
|
ex_label, // ev_void (ick)
|
|
ex_string, // ev_string
|
|
ex_float, // ev_float
|
|
ex_vector, // ev_vector
|
|
ex_entity, // ev_entity
|
|
ex_field, // ev_field
|
|
ex_func, // ev_func
|
|
ex_pointer, // ev_pointer
|
|
ex_quaternion, // ev_quaternion
|
|
ex_int, // ev_int
|
|
};
|
|
|
|
static const char *type_names[] = {
|
|
"void",
|
|
"string",
|
|
"float",
|
|
"vector",
|
|
"entity",
|
|
"field",
|
|
"function",
|
|
"pointer",
|
|
"quaternion",
|
|
"int",
|
|
};
|
|
|
|
static etype_t
|
|
get_type (expr_t *e)
|
|
{
|
|
switch (e->type) {
|
|
case ex_label:
|
|
case ex_block:
|
|
return ev_void;
|
|
case ex_expr:
|
|
case ex_uexpr:
|
|
return e->e.expr.type->type;
|
|
case ex_def:
|
|
return e->e.def->type->type;
|
|
case ex_string:
|
|
case ex_float:
|
|
case ex_vector:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
case ex_quaternion:
|
|
return qc_types[e->type];
|
|
case ex_int: //FIXME?
|
|
e->type = ex_float;
|
|
e->e.float_val = e->e.int_val;
|
|
return ev_float;
|
|
}
|
|
return ev_void;
|
|
}
|
|
|
|
expr_t *
|
|
error (expr_t *e, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
string_t file = s_file;
|
|
int line = pr_source_line;
|
|
|
|
va_start (args, fmt);
|
|
if (e) {
|
|
file = e->file;
|
|
line = e->line;
|
|
}
|
|
fprintf (stderr, "%s:%d: ", strings + file, line);
|
|
vfprintf (stderr, fmt, args);
|
|
fputs ("\n", stderr);
|
|
if (e) {
|
|
e = new_expr ();
|
|
e->type = ex_int;
|
|
}
|
|
va_end (args);
|
|
pr_error_count++;
|
|
return e;
|
|
}
|
|
|
|
void
|
|
warning (expr_t *e, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
string_t file = s_file;
|
|
int line = pr_source_line;
|
|
|
|
if (options.warn_error) {
|
|
options.warn_error = 0; // only want to do this once
|
|
error (e, "warnings treated as errors");
|
|
}
|
|
|
|
va_start (args, fmt);
|
|
if (e) {
|
|
file = e->file;
|
|
line = e->line;
|
|
}
|
|
fprintf (stderr, "%s:%d: warning:", strings + file, line);
|
|
vfprintf (stderr, fmt, args);
|
|
fputs ("\n", stderr);
|
|
va_end (args);
|
|
}
|
|
|
|
expr_t *
|
|
type_mismatch (expr_t *e1, expr_t *e2, int op)
|
|
{
|
|
etype_t t1, t2;
|
|
char opname[4];
|
|
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
|
|
switch (op) {
|
|
case OR:
|
|
opname[0] = '|';
|
|
opname[1] = '|';
|
|
opname[2] = 0;
|
|
break;
|
|
case AND:
|
|
opname[0] = '&';
|
|
opname[1] = '&';
|
|
opname[2] = 0;
|
|
break;
|
|
case EQ:
|
|
opname[0] = '=';
|
|
opname[1] = '=';
|
|
opname[2] = 0;
|
|
break;
|
|
case NE:
|
|
opname[0] = '!';
|
|
opname[1] = '=';
|
|
opname[2] = 0;
|
|
break;
|
|
case LE:
|
|
opname[0] = '<';
|
|
opname[1] = '=';
|
|
opname[2] = 0;
|
|
break;
|
|
case GE:
|
|
opname[0] = '>';
|
|
opname[1] = '=';
|
|
opname[2] = 0;
|
|
break;
|
|
case LT:
|
|
opname[0] = '<';
|
|
opname[1] = 0;
|
|
break;
|
|
case GT:
|
|
opname[0] = '>';
|
|
opname[1] = 0;
|
|
break;
|
|
default:
|
|
opname[0] = op;
|
|
opname[1] = 0;
|
|
break;
|
|
}
|
|
|
|
return error (e1, "type mismatch: %s %s %s",
|
|
type_names[t1], opname, type_names[t2]);
|
|
}
|
|
|
|
expr_t *
|
|
new_expr (void)
|
|
{
|
|
expr_t *e = calloc (1, sizeof (expr_t));
|
|
e->line = pr_source_line;
|
|
e->file = s_file;
|
|
return e;
|
|
}
|
|
|
|
expr_t *
|
|
new_label_expr (void)
|
|
{
|
|
static int label = 0;
|
|
int lnum = ++label;
|
|
const char *fname = current_func->def->name;
|
|
|
|
expr_t *l = new_expr ();
|
|
l->type = ex_label;
|
|
l->e.label.name = malloc (1 + strlen (fname) + 1 + ceil (log10 (lnum)) + 1);
|
|
sprintf (l->e.label.name, "$%s_%d", fname, lnum);
|
|
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 ();
|
|
|
|
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 ();
|
|
|
|
e->type = ex_uexpr;
|
|
e->e.expr.op = op;
|
|
e->e.expr.e1 = e1;
|
|
return e;
|
|
}
|
|
|
|
expr_t *
|
|
append_expr (expr_t *block, expr_t *e)
|
|
{
|
|
if (block->type != ex_block)
|
|
abort ();
|
|
|
|
if (!e)
|
|
return block;
|
|
|
|
*block->e.block.tail = e;
|
|
block->e.block.tail = &e->next;
|
|
|
|
return block;
|
|
}
|
|
|
|
void
|
|
print_expr (expr_t *e)
|
|
{
|
|
printf (" ");
|
|
if (!e) {
|
|
printf ("(nil)");
|
|
return;
|
|
}
|
|
switch (e->type) {
|
|
case ex_label:
|
|
printf ("%s", e->e.label.name);
|
|
break;
|
|
case ex_block:
|
|
printf ("{\n");
|
|
for (e = e->e.block.head; e; e = e->next) {
|
|
print_expr (e);
|
|
puts("");
|
|
}
|
|
printf ("}");
|
|
break;
|
|
case ex_expr:
|
|
print_expr (e->e.expr.e1);
|
|
if (e->e.expr.op == 'c') {
|
|
expr_t *p = e->e.expr.e2;
|
|
printf ("(");
|
|
while (p) {
|
|
print_expr (p);
|
|
if (p->next)
|
|
printf (",");
|
|
p = p->next;
|
|
}
|
|
printf (")");
|
|
} else {
|
|
print_expr (e->e.expr.e2);
|
|
if (isprint (e->e.expr.op)) {
|
|
printf (" %c", e->e.expr.op);
|
|
} else {
|
|
printf (" %d", e->e.expr.op);
|
|
}
|
|
}
|
|
break;
|
|
case ex_uexpr:
|
|
print_expr (e->e.expr.e1);
|
|
if (isprint (e->e.expr.op)) {
|
|
printf (" u%c", e->e.expr.op);
|
|
} else {
|
|
printf (" u%d", e->e.expr.op);
|
|
}
|
|
break;
|
|
case ex_def:
|
|
printf ("%s", e->e.def->name);
|
|
break;
|
|
case ex_string:
|
|
printf ("\"%s\"", e->e.string_val);
|
|
break;
|
|
case ex_float:
|
|
printf ("%g", e->e.float_val);
|
|
break;
|
|
case ex_vector:
|
|
printf ("'%g", e->e.vector_val[0]);
|
|
printf ( " %g", e->e.vector_val[1]);
|
|
printf ( " %g'", e->e.vector_val[2]);
|
|
break;
|
|
case ex_quaternion:
|
|
printf ("'%g", e->e.quaternion_val[0]);
|
|
printf (" %g", e->e.quaternion_val[1]);
|
|
printf (" %g", e->e.quaternion_val[2]);
|
|
printf (" %g'", e->e.quaternion_val[3]);
|
|
break;
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
case ex_int:
|
|
printf ("%d", e->e.int_val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static expr_t *
|
|
do_op_string (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
int len;
|
|
char *buf;
|
|
char *s1, *s2;
|
|
|
|
s1 = e1->e.string_val;
|
|
s2 = e2->e.string_val;
|
|
|
|
switch (op) {
|
|
case '+':
|
|
len = strlen (s1) + strlen (s2) + 1;
|
|
buf = malloc (len);
|
|
strcpy (buf, s1);
|
|
strcat (buf, s2);
|
|
e1->e.string_val = buf;
|
|
break;
|
|
case LT:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) < 0;
|
|
break;
|
|
case GT:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) > 0;
|
|
break;
|
|
case LE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) <= 0;
|
|
break;
|
|
case GE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) >= 0;
|
|
break;
|
|
case EQ:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) == 0;
|
|
break;
|
|
case NE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = strcmp (s1, s2) != 0;
|
|
break;
|
|
default:
|
|
return error (e1, "invalid operand for string");
|
|
}
|
|
return e1;
|
|
}
|
|
|
|
static expr_t *
|
|
do_op_float (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
float f1, f2;
|
|
|
|
f1 = e1->e.float_val;
|
|
f2 = e2->e.float_val;
|
|
|
|
switch (op) {
|
|
case '+':
|
|
e1->e.float_val += f2;
|
|
break;
|
|
case '-':
|
|
e1->e.float_val -= f2;
|
|
break;
|
|
case '*':
|
|
e1->e.float_val *= f2;
|
|
break;
|
|
case '/':
|
|
e1->e.float_val /= f2;
|
|
break;
|
|
case '&':
|
|
e1->e.float_val = (int)f1 & (int)f2;
|
|
break;
|
|
case '|':
|
|
e1->e.float_val += (int)f1 | (int)f2;
|
|
break;
|
|
case AND:
|
|
e1->e.float_val = f1 && f2;
|
|
break;
|
|
case OR:
|
|
e1->e.float_val += f1 || f2;
|
|
break;
|
|
case LT:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 < f2;
|
|
break;
|
|
case GT:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 > f2;
|
|
break;
|
|
case LE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 <= f2;
|
|
break;
|
|
case GE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 >= f2;
|
|
break;
|
|
case EQ:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 == f2;
|
|
break;
|
|
case NE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = f1 != f2;
|
|
break;
|
|
default:
|
|
return error (e1, "invalid operand for string");
|
|
}
|
|
return e1;
|
|
}
|
|
|
|
static expr_t *
|
|
do_op_vector (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
float *v1, *v2;
|
|
|
|
v1 = e1->e.vector_val;
|
|
v2 = e2->e.vector_val;
|
|
|
|
switch (op) {
|
|
case '+':
|
|
VectorAdd (v1, v2, v1);
|
|
break;
|
|
case '-':
|
|
VectorSubtract (v1, v2, v1);
|
|
break;
|
|
case '*':
|
|
e1->type = ex_float;
|
|
e1->e.float_val = DotProduct (v1, v2);
|
|
break;
|
|
case EQ:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = (v1[0] == v2[0])
|
|
&& (v1[1] == v2[1])
|
|
&& (v1[2] == v2[2]);
|
|
break;
|
|
case NE:
|
|
e1->type = ex_int;
|
|
e1->e.int_val = (v1[0] == v2[0])
|
|
|| (v1[1] != v2[1])
|
|
|| (v1[2] != v2[2]);
|
|
break;
|
|
default:
|
|
return error (e1, "invalid operand for string");
|
|
}
|
|
return e1;
|
|
}
|
|
|
|
static expr_t *
|
|
do_op_huh (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
return error (e1, "funny constant");
|
|
}
|
|
|
|
static expr_t *(*do_op[]) (int op, expr_t *e1, expr_t *e2) = {
|
|
do_op_huh,
|
|
do_op_string,
|
|
do_op_float,
|
|
do_op_vector,
|
|
do_op_huh,
|
|
do_op_huh,
|
|
do_op_huh,
|
|
do_op_huh,
|
|
};
|
|
|
|
static expr_t *
|
|
binary_const (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
etype_t t1, t2;
|
|
//expr_t *e;
|
|
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
|
|
if (t1 == t2) {
|
|
return do_op[t1](op, e1, e2);
|
|
} else {
|
|
return type_mismatch (e1, e2, op);
|
|
}
|
|
}
|
|
|
|
static expr_t *
|
|
field_expr (expr_t *e1, expr_t *e2)
|
|
{
|
|
etype_t t1, t2;
|
|
expr_t *e;
|
|
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
|
|
if (t1 != ev_entity || t2 != ev_field) {
|
|
return error (e1, "type missmatch for .");
|
|
}
|
|
|
|
e = new_binary_expr ('.', e1, e2);
|
|
e->e.expr.type = (e2->type == ex_def)
|
|
? e2->e.def->type->aux_type
|
|
: e2->e.expr.type;
|
|
return e;
|
|
}
|
|
|
|
static expr_t *
|
|
test_expr (expr_t *e, int test)
|
|
{
|
|
expr_t *new = 0;
|
|
|
|
if (!test)
|
|
return unary_expr ('!', e);
|
|
|
|
switch (get_type (e)) {
|
|
case ev_int://FIXME
|
|
case ev_type_count:
|
|
case ev_void:
|
|
error (e, "internal error");
|
|
abort ();
|
|
case ev_string:
|
|
new = new_expr ();
|
|
new->type = ex_string;
|
|
break;
|
|
case ev_float:
|
|
return e;
|
|
case ev_vector:
|
|
new = new_expr ();
|
|
new->type = ex_vector;
|
|
break;
|
|
case ev_entity:
|
|
new = new_expr ();
|
|
new->type = ex_entity;
|
|
break;
|
|
case ev_field:
|
|
new = new_expr ();
|
|
new->type = ex_field;
|
|
break;
|
|
case ev_func:
|
|
new = new_expr ();
|
|
new->type = ex_func;
|
|
break;
|
|
case ev_pointer:
|
|
new = new_expr ();
|
|
new->type = ex_pointer;
|
|
break;
|
|
case ev_quaternion:
|
|
new = new_expr ();
|
|
new->type = ex_quaternion;
|
|
break;
|
|
}
|
|
return binary_expr (NE, e, new);
|
|
}
|
|
|
|
expr_t *
|
|
binary_expr (int op, expr_t *e1, expr_t *e2)
|
|
{
|
|
etype_t t1, t2;
|
|
|
|
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_string && e2->type >= ex_string)
|
|
return binary_const (op, e1, e2);
|
|
|
|
if ((op == '&' || op == '|')
|
|
&& e1->type == ex_uexpr && e1->e.expr.op == '!' && !e1->paren) {
|
|
warning (e1, "ambiguous logic. Suggest explicit parentheses with expressions involving ! and %c", op);
|
|
}
|
|
|
|
t1 = get_type (e1);
|
|
t2 = get_type (e2);
|
|
if (t1 == ev_void || t2 == ev_void) {
|
|
error (e1, "internal error");
|
|
abort ();
|
|
}
|
|
|
|
if (t1 == t2) {
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
if ((op >= OR && op <= GT) || (op == '*' && t1 == ev_vector))
|
|
e->e.expr.type = &type_float;
|
|
else
|
|
e->e.expr.type = types[t1];
|
|
if (op == '=' && e1->type == ex_expr && e1->e.expr.op == '.') {
|
|
e1->e.expr.type = &type_pointer;
|
|
}
|
|
return e;
|
|
} else {
|
|
switch (t1) {
|
|
case ev_float:
|
|
if (t2 == ev_vector) {
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_vector;
|
|
return e;
|
|
} else {
|
|
goto type_mismatch;
|
|
}
|
|
case ev_vector:
|
|
if (t2 == ev_float) {
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = &type_vector;
|
|
return e;
|
|
} else {
|
|
goto type_mismatch;
|
|
}
|
|
case ev_field:
|
|
if (e1->e.expr.type->aux_type->type == t2) {
|
|
expr_t *e = new_binary_expr (op, e1, e2);
|
|
e->e.expr.type = e->e.expr.type->aux_type;
|
|
return e;
|
|
} else {
|
|
goto type_mismatch;
|
|
}
|
|
default:
|
|
type_mismatch:
|
|
return type_mismatch (e1, e2, op);
|
|
}
|
|
}
|
|
}
|
|
|
|
expr_t *
|
|
unary_expr (int op, expr_t *e)
|
|
{
|
|
switch (op) {
|
|
case '-':
|
|
switch (e->type) {
|
|
case ex_label:
|
|
case ex_block:
|
|
abort ();
|
|
case ex_uexpr:
|
|
if (e->e.expr.op == '-')
|
|
return e->e.expr.e1;
|
|
case ex_expr:
|
|
case ex_def:
|
|
{
|
|
expr_t *n = new_unary_expr (op, e);
|
|
n->e.expr.type = (e->type == ex_def)
|
|
? e->e.def->type
|
|
: e->e.expr.type;
|
|
return n;
|
|
}
|
|
case ex_int:
|
|
e->e.int_val *= -1;
|
|
return e;
|
|
case ex_float:
|
|
e->e.float_val *= -1;
|
|
return e;
|
|
case ex_string:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
return error (e, "invalid type for unary -");
|
|
case ex_vector:
|
|
e->e.vector_val[0] *= -1;
|
|
e->e.vector_val[1] *= -1;
|
|
e->e.vector_val[2] *= -1;
|
|
return e;
|
|
case ex_quaternion:
|
|
e->e.quaternion_val[0] *= -1;
|
|
e->e.quaternion_val[1] *= -1;
|
|
e->e.quaternion_val[2] *= -1;
|
|
e->e.quaternion_val[3] *= -1;
|
|
return e;
|
|
}
|
|
break;
|
|
case '!':
|
|
switch (e->type) {
|
|
case ex_label:
|
|
case ex_block:
|
|
abort ();
|
|
case ex_uexpr:
|
|
case ex_expr:
|
|
case ex_def:
|
|
{
|
|
expr_t *n = new_unary_expr (op, e);
|
|
n->e.expr.type = &type_float;
|
|
return n;
|
|
}
|
|
case ex_int:
|
|
e->e.int_val = !e->e.int_val;
|
|
return e;
|
|
case ex_float:
|
|
e->e.int_val = !e->e.float_val;
|
|
e->type = ex_int;
|
|
return e;
|
|
case ex_string:
|
|
e->e.int_val = !e->e.string_val || !e->e.string_val[0];
|
|
e->type = ex_int;
|
|
return e;
|
|
case ex_vector:
|
|
e->e.int_val = !e->e.vector_val[0]
|
|
&& !e->e.vector_val[1]
|
|
&& !e->e.vector_val[2];
|
|
e->type = ex_int;
|
|
return e;
|
|
case ex_quaternion:
|
|
e->e.int_val = !e->e.quaternion_val[0]
|
|
&& !e->e.quaternion_val[1]
|
|
&& !e->e.quaternion_val[2]
|
|
&& !e->e.quaternion_val[3];
|
|
e->type = ex_int;
|
|
return e;
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
error (e, "internal error");
|
|
abort ();
|
|
}
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
error (e, "internal error");
|
|
abort ();
|
|
}
|
|
|
|
expr_t *
|
|
function_expr (expr_t *e1, expr_t *e2)
|
|
{
|
|
etype_t t1;
|
|
expr_t *e;
|
|
int parm_count = 0;
|
|
type_t *ftype;
|
|
|
|
t1 = get_type (e1);
|
|
|
|
if (t1 != ev_func) {
|
|
if (e1->type == ex_def)
|
|
return error (e1, "Called object \"%s\" is not a function",
|
|
e1->e.def->name);
|
|
else
|
|
return error (e1, "Called object is not a function");
|
|
}
|
|
|
|
if (e1->type == ex_def && e2 && e2->type == ex_string) {
|
|
//XXX eww, I hate this, but it's needed :(
|
|
def_t *func = e1->e.def;
|
|
def_t *e = PR_ReuseConstant (e2, 0);
|
|
|
|
if (strncmp (func->name, "precache_sound", 14) == 0)
|
|
PrecacheSound (e, func->name[4]);
|
|
else if (strncmp (func->name, "precache_model", 14) == 0)
|
|
PrecacheModel (e, func->name[14]);
|
|
else if (strncmp (func->name, "precache_file", 13) == 0)
|
|
PrecacheFile (e, func->name[13]);
|
|
}
|
|
|
|
ftype = e1->type == ex_def
|
|
? e1->e.def->type
|
|
: e1->e.expr.type;
|
|
|
|
for (e = e2; e; e = e->next)
|
|
parm_count++;
|
|
if (parm_count > 8) {
|
|
return error (e1, "more than 8 parameters");
|
|
}
|
|
if (ftype->num_parms != -1) {
|
|
if (parm_count > ftype->num_parms) {
|
|
return error (e1, "too many arguments");
|
|
} else if (parm_count < ftype->num_parms) {
|
|
return error (e1, "too few arguments");
|
|
}
|
|
}
|
|
e = new_binary_expr ('c', e1, e2);
|
|
e->e.expr.type = ftype->aux_type;
|
|
return e;
|
|
}
|
|
|
|
def_t *
|
|
emit_statement (int sline, opcode_t *op, def_t *var_a, def_t *var_b, def_t *var_c)
|
|
{
|
|
dstatement_t *statement;
|
|
def_t *ret;
|
|
|
|
if (options.debug) {
|
|
int line = sline - lineno_base;
|
|
|
|
if (line != linenos[num_linenos - 1].line) {
|
|
pr_lineno_t *lineno = new_lineno ();
|
|
lineno->line = line;
|
|
lineno->fa.addr = numstatements;
|
|
}
|
|
}
|
|
statement = &statements[numstatements];
|
|
numstatements++;
|
|
statement_linenums[statement - statements] = pr_source_line;
|
|
statement->op = op->opcode;
|
|
statement->a = var_a ? var_a->ofs : 0;
|
|
statement->b = var_b ? var_b->ofs : 0;
|
|
if (op->type_c == ev_void || op->right_associative) {
|
|
// ifs, gotos, and assignments don't need vars allocated
|
|
var_c = NULL;
|
|
statement->c = 0;
|
|
ret = var_a;
|
|
} else { // allocate result space
|
|
if (!var_c)
|
|
var_c = PR_GetTempDef (types[op->type_c], pr_scope);
|
|
statement->c = var_c->ofs;
|
|
ret = var_c;
|
|
}
|
|
PR_AddStatementRef (var_a, statement, 0);
|
|
PR_AddStatementRef (var_b, statement, 1);
|
|
PR_AddStatementRef (var_c, statement, 2);
|
|
|
|
if (op->right_associative)
|
|
return var_a;
|
|
return var_c;
|
|
}
|
|
|
|
def_t *emit_sub_expr (expr_t *e, def_t *dest);
|
|
|
|
void
|
|
emit_branch (int line, opcode_t *op, expr_t *e, expr_t *l)
|
|
{
|
|
dstatement_t *st;
|
|
statref_t *ref;
|
|
def_t *def = 0;
|
|
|
|
if (e)
|
|
def = emit_sub_expr (e, 0);
|
|
st = &statements[numstatements];
|
|
emit_statement (line, op, def, 0, 0);
|
|
if (l->e.label.statement) {
|
|
if (op == op_goto)
|
|
st->a = l->e.label.statement - st;
|
|
else
|
|
st->b = l->e.label.statement - st;
|
|
} else {
|
|
ref = PR_NewStatref (st, op != op_goto);
|
|
ref->next = l->e.label.refs;
|
|
l->e.label.refs = ref;
|
|
}
|
|
}
|
|
|
|
def_t *
|
|
emit_function_call (expr_t *e, def_t *dest)
|
|
{
|
|
def_t *func = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_t parm;
|
|
def_t *arg;
|
|
expr_t *earg;
|
|
opcode_t *op;
|
|
int count = 0, ind;
|
|
|
|
for (earg = e->e.expr.e2; earg; earg = earg->next)
|
|
count++;
|
|
ind = count;
|
|
for (earg = e->e.expr.e2; earg; earg = earg->next) {
|
|
ind--;
|
|
parm = def_parms[ind];
|
|
parm.type = types[get_type (earg)];
|
|
arg = emit_sub_expr (earg, &parm);
|
|
if (earg->type != ex_expr) {
|
|
op = PR_Opcode_Find ("=", 5, arg, &parm, &parm);
|
|
emit_statement (e->line, op, arg, &parm, 0);
|
|
}
|
|
}
|
|
op = PR_Opcode_Find (va ("<CALL%d>", count), -1, &def_function, &def_void, &def_void);
|
|
emit_statement (e->line, op, func, 0, 0);
|
|
|
|
def_ret.type = func->type->aux_type;
|
|
if (dest) {
|
|
op = PR_Opcode_Find ("=", 5, dest, &def_ret, &def_ret);
|
|
emit_statement (e->line, op, &def_ret, dest, 0);
|
|
return dest;
|
|
} else {
|
|
return &def_ret;
|
|
}
|
|
}
|
|
|
|
def_t *
|
|
emit_assign_expr (expr_t *e, def_t *dest)
|
|
{
|
|
def_t *def_a, *def_b;
|
|
opcode_t *op;
|
|
expr_t *e1 = e->e.expr.e1;
|
|
expr_t *e2 = e->e.expr.e2;
|
|
|
|
def_a = emit_sub_expr (e1, 0);
|
|
if (def_a->type == &type_pointer) {
|
|
def_b = emit_sub_expr (e2, 0);
|
|
op = PR_Opcode_Find ("=", 5, def_a, def_b, def_b);
|
|
emit_statement (e->line, op, def_b, def_a, 0);
|
|
} else {
|
|
if (def_a->initialized) {
|
|
if (options.cow) {
|
|
int size = type_size [def_a->type->type];
|
|
int ofs = PR_NewLocation (def_a->type);
|
|
memcpy (pr_globals + ofs, pr_globals + def_a->ofs, size);
|
|
def_a->ofs = ofs;
|
|
def_a->initialized = 0;
|
|
warning (e1, "assignment to constant %s", def_a->name);
|
|
} else {
|
|
error (e1, "assignment to constant %s", def_a->name);
|
|
}
|
|
}
|
|
if (e2->type == ex_expr) {
|
|
def_b = emit_sub_expr (e2, def_a);
|
|
} else {
|
|
def_b = emit_sub_expr (e2, 0);
|
|
op = PR_Opcode_Find ("=", 5, def_a, def_b, def_b);
|
|
emit_statement (e->line, op, def_b, def_a, 0);
|
|
}
|
|
if (!def_b->name)
|
|
dest = def_a;
|
|
if (dest) {
|
|
op = PR_Opcode_Find ("=", 5, dest, def_b, def_b);
|
|
emit_statement (e->line, op, def_b, dest, 0);
|
|
}
|
|
}
|
|
return def_b;
|
|
}
|
|
|
|
def_t *
|
|
emit_sub_expr (expr_t *e, def_t *dest)
|
|
{
|
|
opcode_t *op;
|
|
char *operator;
|
|
def_t *def_a, *def_b;
|
|
int priority;
|
|
|
|
switch (e->type) {
|
|
case ex_label:
|
|
case ex_block:
|
|
error (e, "internal error");
|
|
abort ();
|
|
case ex_expr:
|
|
if (e->e.expr.op == 'c')
|
|
return emit_function_call (e, dest);
|
|
if (e->e.expr.op == '=')
|
|
return emit_assign_expr (e, dest);
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e2, 0);
|
|
switch (e->e.expr.op) {
|
|
case AND:
|
|
operator = "&&";
|
|
priority = 6;
|
|
break;
|
|
case OR:
|
|
operator = "||";
|
|
priority = 6;
|
|
break;
|
|
case EQ:
|
|
operator = "==";
|
|
priority = 4;
|
|
break;
|
|
case NE:
|
|
operator = "!=";
|
|
priority = 4;
|
|
break;
|
|
case LE:
|
|
operator = "<=";
|
|
priority = 4;
|
|
break;
|
|
case GE:
|
|
operator = ">=";
|
|
priority = 4;
|
|
break;
|
|
case LT:
|
|
operator = "<";
|
|
priority = 4;
|
|
break;
|
|
case GT:
|
|
operator = ">";
|
|
priority = 4;
|
|
break;
|
|
case '+':
|
|
operator = "+";
|
|
priority = 3;
|
|
break;
|
|
case '-':
|
|
operator = "-";
|
|
priority = 3;
|
|
break;
|
|
case '*':
|
|
operator = "*";
|
|
priority = 2;
|
|
break;
|
|
case '/':
|
|
operator = "/";
|
|
priority = 2;
|
|
break;
|
|
case '&':
|
|
operator = "&";
|
|
priority = 2;
|
|
break;
|
|
case '|':
|
|
operator = "|";
|
|
priority = 2;
|
|
break;
|
|
case '.':
|
|
operator = ".";
|
|
priority = 1;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
if (!dest)
|
|
dest = PR_GetTempDef (e->e.expr.type, pr_scope);
|
|
op = PR_Opcode_Find (operator, priority, def_a, def_b, dest);
|
|
return emit_statement (e->line, op, def_a, def_b, dest);
|
|
case ex_uexpr:
|
|
if (e->e.expr.op == '!') {
|
|
operator = "!";
|
|
priority = -1;
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = &def_void;
|
|
} else if (e->e.expr.op == '-') {
|
|
static expr_t zero;
|
|
|
|
zero.type = expr_types[get_type (e->e.expr.e1)];
|
|
|
|
operator = "-";
|
|
priority = 3;
|
|
def_a = PR_ReuseConstant (&zero, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e1, 0);
|
|
} else {
|
|
abort ();
|
|
}
|
|
op = PR_Opcode_Find (operator, priority, def_a, def_b, dest);
|
|
return emit_statement (e->line, op, def_a, def_b, dest);
|
|
case ex_def:
|
|
return e->e.def;
|
|
case ex_string:
|
|
case ex_float:
|
|
case ex_vector:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
case ex_quaternion:
|
|
case ex_int:
|
|
return PR_ReuseConstant (e, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
emit_expr (expr_t *e)
|
|
{
|
|
def_t *def;
|
|
def_t *def_a;
|
|
def_t *def_b;
|
|
statref_t *ref;
|
|
label_t *label;
|
|
//opcode_t *op;
|
|
|
|
switch (e->type) {
|
|
case ex_label:
|
|
label = &e->e.label;
|
|
label->statement = &statements[numstatements];
|
|
for (ref = label->refs; ref; ref = ref->next) {
|
|
switch (ref->field) {
|
|
case 0:
|
|
ref->statement->a = label->statement - ref->statement;
|
|
break;
|
|
case 1:
|
|
ref->statement->b = label->statement - ref->statement;
|
|
break;
|
|
case 2:
|
|
ref->statement->c = label->statement - ref->statement;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
break;
|
|
case ex_block:
|
|
for (e = e->e.block.head; e; e = e->next)
|
|
emit_expr (e);
|
|
break;
|
|
case ex_expr:
|
|
switch (e->e.expr.op) {
|
|
case '=':
|
|
emit_assign_expr (e, 0);
|
|
break;
|
|
case 'n':
|
|
emit_branch (e->line, op_ifnot, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case 'i':
|
|
emit_branch (e->line, op_if, e->e.expr.e1, e->e.expr.e2);
|
|
break;
|
|
case 'c':
|
|
emit_function_call (e, 0);
|
|
break;
|
|
case 's':
|
|
def_a = emit_sub_expr (e->e.expr.e1, 0);
|
|
def_b = emit_sub_expr (e->e.expr.e2, 0);
|
|
emit_statement (e->line, op_state, def_a, def_b, 0);
|
|
break;
|
|
default:
|
|
warning (e, "Ignoring useless expression");
|
|
break;
|
|
}
|
|
break;
|
|
case ex_uexpr:
|
|
switch (e->e.expr.op) {
|
|
case 'r':
|
|
def = 0;
|
|
if (e->e.expr.e1)
|
|
def = emit_sub_expr (e->e.expr.e1, 0);
|
|
emit_statement (e->line, op_return, def, 0, 0);
|
|
break;
|
|
case 'g':
|
|
emit_branch (e->line, op_goto, 0, e->e.expr.e1);
|
|
break;
|
|
default:
|
|
warning (e, "Ignoring useless expression");
|
|
emit_expr (e->e.expr.e1);
|
|
break;
|
|
}
|
|
break;
|
|
case ex_def:
|
|
case ex_string:
|
|
case ex_float:
|
|
case ex_vector:
|
|
case ex_entity:
|
|
case ex_field:
|
|
case ex_func:
|
|
case ex_pointer:
|
|
case ex_quaternion:
|
|
case ex_int:
|
|
warning (e, "Ignoring useless expression");
|
|
break;
|
|
}
|
|
PR_FreeTempDefs ();
|
|
}
|