
//**************************************************************************
//**
//** comp.c
//**
//** $Header: /H2 Mission Pack/Tools/hcc/PR_COMP.C 2     2/12/98 3:19p Jmonroe $
//**
//**************************************************************************

// HEADER FILES ------------------------------------------------------------

#include "hcc.h"

// MACROS ------------------------------------------------------------------

#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2))
#define STRNCMP(s1,s2,n) (((*s1)!=(*s2)) || strncmp(s1,s2,n))
#define MAX_ARRAY_ELEMENTS 1024
#define SPECIAL_ASSIGN_OP 500

// TYPES -------------------------------------------------------------------

struct hash_element
{
	struct hash_element *next;
	def_t *def;
};

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------

// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------

static void InitHashTable(void);
static void ParseUnion(void);
static type_t *ParseUnionType(void);
static void ParseFunctionDef(def_t *def, type_t *type);
static void ParseCStyleFunctionDef(char *funcName, type_t *type);
static def_t *GetArrayDef(char *name, type_t *type, int *elementCount);
static def_t *GetFieldArrayDef(char *name, type_t *type,
	int *elementCount);
static int ParseArray(char *name, type_t *type);
static def_t *NewVarDef(char *name, type_t *type);
static function_t *ParseImmediateStatements(type_t *type);
static void ParseState(void);

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

// PUBLIC DATA DEFINITIONS -------------------------------------------------

// PRIVATE DATA DEFINITIONS ------------------------------------------------

static int FrameIndex;
static struct hash_element *HashTable[MS_HASH_TABLE_SIZE];

pr_info_t pr;
def_t *pr_global_defs[MAX_REGS];	// to find def for a global variable

def_t *pr_scope; // the function being parsed, or NULL

string_t s_file; // filename for function definition

int locals_end; // for tracking local variables vs temps

jmp_buf pr_parse_abort; // longjump with this on parse error

opcode_t pr_opcodes[] =
{
	{"<DONE>", "DONE", -1, false, &def_entity, &def_field, &def_void, 0},

	{"*", "MUL_F", 2, false, &def_float, &def_float, &def_float, 1},
	{"*", "MUL_V", 2, false, &def_vector, &def_vector, &def_float, 1},
	{"*", "MUL_FV", 2, false, &def_float, &def_vector, &def_vector, 1},
	{"*", "MUL_VF", 2, false, &def_vector, &def_float, &def_vector, 1},

	{"/", "DIV", 2, false, &def_float, &def_float, &def_float, 2},

	{"+", "ADD_F", 3, false, &def_float, &def_float, &def_float, 3},
	{"+", "ADD_V", 3, false, &def_vector, &def_vector, &def_vector, 3},

	{"-", "SUB_F", 3, false, &def_float, &def_float, &def_float, 4},
	{"-", "SUB_V", 3, false, &def_vector, &def_vector, &def_vector, 4},

	{"==", "EQ_F", 4, false, &def_float, &def_float, &def_float, 5},
	{"==", "EQ_V", 4, false, &def_vector, &def_vector, &def_float, 5},
	{"==", "EQ_S", 4, false, &def_string, &def_string, &def_float, 5},
	{"==", "EQ_E", 4, false, &def_entity, &def_entity, &def_float, 5},
	{"==", "EQ_FNC", 4, false, &def_function, &def_function, &def_float, 5},

	{"!=", "NE_F", 4, false, &def_float, &def_float, &def_float, 6},
	{"!=", "NE_V", 4, false, &def_vector, &def_vector, &def_float, 6},
	{"!=", "NE_S", 4, false, &def_string, &def_string, &def_float, 6},
	{"!=", "NE_E", 4, false, &def_entity, &def_entity, &def_float, 6},
	{"!=", "NE_FNC", 4, false, &def_function, &def_function, &def_float, 6},

	{"<=", "LE", 4, false, &def_float, &def_float, &def_float, 7},

	{">=", "GE", 4, false, &def_float, &def_float, &def_float, 8},

	{"<", "LT", 4, false, &def_float, &def_float, &def_float, 9},

	{">", "GT", 4, false, &def_float, &def_float, &def_float, 10},

	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_float, 11},
	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_vector, 11},
	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_string, 11},
	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_entity, 11},
	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_field, 11},
	{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_function, 11},

	{".", "ADDRESS", 1, false, &def_entity, &def_field, &def_pointer, 11},

	{"=", "STORE_F", 5, true, &def_float, &def_float, &def_float, 12},
	{"=", "STORE_V", 5, true, &def_vector, &def_vector, &def_vector, 12},
	{"=", "STORE_S", 5, true, &def_string, &def_string, &def_string, 12},
	{"=", "STORE_ENT", 5, true, &def_entity, &def_entity, &def_entity, 12},
	{"=", "STORE_FLD", 5, true, &def_field, &def_field, &def_field, 12},
	{"=", "STORE_FNC", 5, true, &def_function, &def_function, &def_function, 12},

	{"=", "STOREP_F", 5, true, &def_pointer, &def_float, &def_float, 12},
	{"=", "STOREP_V", 5, true, &def_pointer, &def_vector, &def_vector, 12},
	{"=", "STOREP_S", 5, true, &def_pointer, &def_string, &def_string, 12},
	{"=", "STOREP_ENT", 5, true, &def_pointer, &def_entity, &def_entity, 12},
	{"=", "STOREP_FLD", 5, true, &def_pointer, &def_field, &def_field, 12},
	{"=", "STOREP_FNC", 5, true, &def_pointer, &def_function, &def_function, 12},

	{"<RETURN>", "RETURN", -1, false, &def_void, &def_void, &def_void, 13},

	{"!", "NOT_F", -1, false, &def_float, &def_void, &def_float, 14},
	{"!", "NOT_V", -1, false, &def_vector, &def_void, &def_float, 14},
	{"!", "NOT_S", -1, false, &def_vector, &def_void, &def_float, 14},
	{"!", "NOT_ENT", -1, false, &def_entity, &def_void, &def_float, 14},
	{"!", "NOT_FNC", -1, false, &def_function, &def_void, &def_float, 14},

	{"<IF>", "IF", -1, false, &def_float, &def_float, &def_void, 15},

	{"<IFNOT>", "IFNOT", -1, false, &def_float, &def_float, &def_void, 16},

	{"<CALL0>", "CALL0", -1, false, &def_function, &def_void, &def_void, 17},
	{"<CALL1>", "CALL1", -1, false, &def_function, &def_void, &def_void, 18},
	{"<CALL2>", "CALL2", -1, false, &def_function, &def_void, &def_void, 19}, 
	{"<CALL3>", "CALL3", -1, false, &def_function, &def_void, &def_void, 20}, 
	{"<CALL4>", "CALL4", -1, false, &def_function, &def_void, &def_void, 21},
	{"<CALL5>", "CALL5", -1, false, &def_function, &def_void, &def_void, 22},
	{"<CALL6>", "CALL6", -1, false, &def_function, &def_void, &def_void, 23},
	{"<CALL7>", "CALL7", -1, false, &def_function, &def_void, &def_void, 24},
	{"<CALL8>", "CALL8", -1, false, &def_function, &def_void, &def_void, 25},

	{"<STATE>", "STATE", -1, false, &def_float, &def_float, &def_void, 26},

	{"<GOTO>", "GOTO", -1, false, &def_float, &def_void, &def_void, 27},

	{"&&", "AND", 6, false, &def_float, &def_float, &def_float, 28},

	{"||", "OR", 6, false, &def_float, &def_float, &def_float, 29},

	{"&", "BITAND", 2, false, &def_float, &def_float, &def_float, 30},

	{"|", "BITOR", 2, false, &def_float, &def_float, &def_float, 31},

	{"*=", "MULSTORE_F", 5, true,
		&def_float, &def_float, &def_float, 32+SPECIAL_ASSIGN_OP},
	{"*=", "MULSTORE_V", 5, true,
		&def_vector, &def_float, &def_vector, 32+SPECIAL_ASSIGN_OP},
	{"*=", "MULSTOREP_F", 5, true,
		&def_pointer, &def_float, &def_float, 32+SPECIAL_ASSIGN_OP},
	{"*=", "MULSTOREP_V", 5, true,
		&def_pointer, &def_float, &def_vector, 32+SPECIAL_ASSIGN_OP},

	{"/=", "DIVSTORE_F", 5, true,
		&def_float, &def_float, &def_float, 33+SPECIAL_ASSIGN_OP},
	{"/=", "DIVSTOREP_F", 5, true,
		&def_pointer, &def_float, &def_float, 33+SPECIAL_ASSIGN_OP},

	{"+=", "ADDSTORE_F", 5, true,
		&def_float, &def_float, &def_float, 34+SPECIAL_ASSIGN_OP},
	{"+=", "ADDSTORE_V", 5, true,
		&def_vector, &def_vector, &def_vector, 34+SPECIAL_ASSIGN_OP},
	{"+=", "ADDSTOREP_F", 5, true,
		&def_pointer, &def_float, &def_float, 34+SPECIAL_ASSIGN_OP},
	{"+=", "ADDSTOREP_V", 5, true,
		&def_pointer, &def_vector, &def_vector, 34+SPECIAL_ASSIGN_OP},

	{"-=", "SUBSTORE_F", 5, true,
		&def_float, &def_float, &def_float, 35+SPECIAL_ASSIGN_OP},
	{"-=", "SUBSTORE_V", 5, true,
		&def_vector, &def_vector, &def_vector, 35+SPECIAL_ASSIGN_OP},
	{"-=", "SUBSTOREP_F", 5, true,
		&def_pointer, &def_float, &def_float, 35+SPECIAL_ASSIGN_OP},
	{"-=", "SUBSTOREP_V", 5, true,
		&def_pointer, &def_vector, &def_vector, 35+SPECIAL_ASSIGN_OP},

	{"<FETCH_GBL_F>", "FETCH_GBL_F",
		-1, false, &def_float, &def_float, &def_float, 36},
	{"<FETCH_GBL_V>", "FETCH_GBL_V",
		-1, false, &def_vector, &def_float, &def_vector, 36},
	{"<FETCH_GBL_S>", "FETCH_GBL_S",
		-1, false, &def_string, &def_float, &def_string, 36},
	{"<FETCH_GBL_E>", "FETCH_GBL_E",
		-1, false, &def_entity, &def_float, &def_entity, 36},
	{"<FETCH_GBL_FNC>", "FETCH_GBL_FNC",
		-1, false, &def_function, &def_float, &def_function, 36},

	{"<CSTATE>", "CSTATE", -1, false,
		&def_float, &def_float, &def_void, 37},

	{"<CWSTATE>", "CWSTATE", -1, false,
		&def_float, &def_float, &def_void, 38},

	{"<THINKTIME>", "THINKTIME", -1, false,
		&def_entity, &def_float, &def_void, 39},

	{"(+)", "BITSET", 5, true, &def_float, &def_float, &def_float, 40},
	{"(+)", "BITSETP", 5, true, &def_pointer, &def_float, &def_float, 40},
	{"(-)", "BITCLR", 5, true, &def_float, &def_float, &def_float, 41},
	{"(-)", "BITCLRP", 5, true, &def_pointer, &def_float, &def_float, 41},

	{"<RAND0>", "RAND0", -1, false,
		&def_void, &def_void, &def_void, 42},
	{"<RAND1>", "RAND1", -1, false,
		&def_float, &def_void, &def_void, 43},
	{"<RAND2>", "RAND2", -1, false,
		&def_float, &def_float, &def_void, 44},
	{"<RANDV0>", "RANDV0", -1, false,
		&def_void, &def_void, &def_void, 42},
	{"<RANDV1>", "RANDV1", -1, false,
		&def_vector, &def_void, &def_void, 43},
	{"<RANDV2>", "RANDV2", -1, false,
		&def_vector, &def_vector, &def_void, 44},

	{"<SWITCH_F>", "SWITCH_F", -1, false,
		&def_void, &def_void, &def_void, 45},
	{"<SWITCH_V>", "SWITCH_V", -1, false,
		&def_void, &def_void, &def_void, 46},
	{"<SWITCH_S>", "SWITCH_S", -1, false,
		&def_void, &def_void, &def_void, 47},
	{"<SWITCH_E>", "SWITCH_E", -1, false,
		&def_void, &def_void, &def_void, 48},
	{"<SWITCH_FNC>", "SWITCH_FNC", -1, false,
		&def_void, &def_void, &def_void, 49},

	{"<CASE>", "CASE", -1, false,
		&def_void, &def_void, &def_void, 50},
	{"<CASERANGE>", "CASERANGE", -1, false,
		&def_void, &def_void, &def_void, 51},

/*
	{"<FETCH_FLD_F>", "FETCH_FLD_F",
		-1, false, &def_float, &def_float, &def_float, 36},
	{"<FETCH_FLD_V>", "FETCH_FLD_V",
		-1, false, &def_vector, &def_float, &def_vector, 36},
	{"<FETCH_FLD_S>", "FETCH_FLD_S",
		-1, false, &def_string, &def_float, &def_string, 36},
	{"<FETCH_FLD_E>", "FETCH_FLD_E",
		-1, false, &def_entity, &def_float, &def_entity, 36},
	{"<FETCH_FLD_FNC>", "FETCH_FLD_FNC",
		-1, false, &def_function, &def_float, &def_function, 36},
*/

	{NULL}
};

// CODE --------------------------------------------------------------------

//==========================================================================
//
// CO_Init
//
//==========================================================================

void CO_Init(void)
{
	InitHashTable();
}

//==========================================================================
//
// InitHashTable
//
//==========================================================================

static void InitHashTable(void)
{
	memset(HashTable, 0, sizeof(HashTable));
}

//==========================================================================
//
// CO_CompileFile
//
//==========================================================================

qboolean CO_CompileFile(char *fileText, char *fileName)
{
	qboolean inProgress;

	FrameIndex = -1;
	inProgress = false;
	s_file = CopyString(fileName);

	// Ugly hack to prevent longjmp failure from within
	// LX_NewSourceFile().
	if(setjmp(pr_parse_abort))
	{
		LX_ErrorRecovery();
		if(pr_token_type == tt_eof)
		{
			return false;
		}
		pr_error_count++;
		inProgress = true;
	}

	if(inProgress == false)
	{
		LX_NewSourceFile(fileText);
	}

	while(pr_token_type != tt_eof)
	{
		if(setjmp(pr_parse_abort))
		{
			if(++pr_error_count >= MAX_ERRORS)
			{
				printf("stopped at %d errors\n", pr_error_count);
				return false;
			}
			LX_ErrorRecovery();
			if(pr_token_type == tt_eof)
			{
				return false;
			}
		}
		pr_scope = NULL;
		CO_ParseDefs();
	}
	return (pr_error_count == 0);
}

//==========================================================================
//
// CO_GenCode
//
// Emits a primitive statement, returning the def it places its value in.
//
//==========================================================================

def_t *CO_GenCode(opcode_t *op, def_t *var_a, def_t *var_b)
{
	dstatement_t *statement;
	def_t *var_c;

	statement = &statements[numstatements];
	numstatements++;

	statement_linenums[statement-statements] = lx_SourceLine;
	statement->op = op - pr_opcodes;
	statement->a = var_a ? var_a->ofs : 0;
	statement->b = var_b ? var_b->ofs : 0;

	if(op->type_c == &def_void)
	{
		statement->c = 0;
		return NULL;
	}

	if(op->right_associative)
	{
		if(op->tag < SPECIAL_ASSIGN_OP)
		{ // Return right side operand for normal assigments.
			statement->c = 0;
			return var_a;
		}
		if(op->type_a != &def_pointer)
		{
			// Return left side operand for non-pointer special
			// assignments.  Special assignments to pointers need to
			// allocate a register because the result is not accessible
			// if only stored in an entity.
			statement->c = 0;
			return var_b;
		}
	}

	// Allocate the result register.
	var_c = malloc(sizeof(def_t));
	memset(var_c, 0, sizeof(def_t));
	var_c->ofs = numpr_globals;
	var_c->type = op->type_c->type;

	statement->c = numpr_globals;
	numpr_globals += type_size[op->type_c->type->type];

	return var_c;
}

//==========================================================================
//
// CO_GenCodeDirect
//
//==========================================================================

void CO_GenCodeDirect(opcode_t *op, def_t *var_a, def_t *var_b,
	def_t *var_c)
{
	dstatement_t *statement;

	statement = &statements[numstatements];
	numstatements++;

	statement_linenums[statement-statements] = lx_SourceLine;
	statement->op = op - pr_opcodes;
	statement->a = var_a ? var_a->ofs : 0;
	statement->b = var_b ? var_b->ofs : 0;
	statement->c = var_c ? var_c->ofs : 0;
}

//==========================================================================
//
// CO_ParseImmediate
//
// Looks for a preexisting constant.
//
//==========================================================================

def_t *CO_ParseImmediate(void)
{
	def_t *cn = NULL;
	int index = 0;
	struct hash_element *cell = NULL;

	if(pr_immediate_type == &type_string)
	{
		index = MS_Hash(pr_immediate_string);
	}
	else if(pr_immediate_type == &type_float)
	{
		char tmpchar[40];
		sprintf(tmpchar, "%.4f", pr_immediate._float);
		index = MS_Hash(tmpchar);
	}
	else if(pr_immediate_type == &type_vector)
	{
		char tmpchar[80];
		sprintf(tmpchar, "%.4f,%.4f,%.4f",
			pr_immediate.vector[0], pr_immediate.vector[1],
			pr_immediate.vector[2]);
		index = MS_Hash(tmpchar);
	}
	else
	{
		MS_ParseError("weird immediate type");
	}

	for(cell = HashTable[index]; cell != NULL; cell = cell->next)
	{
		cn = cell->def;
		if(!cn->initialized || (cn->type != pr_immediate_type))
		{
			continue;
		}
		if(pr_immediate_type == &type_string)
		{
			if(!STRCMP(G_STRING(cn->ofs), pr_immediate_string))
			{
				return cn;
			}
		}
		else if(pr_immediate_type == &type_float)
		{
			if(G_FLOAT(cn->ofs) == pr_immediate._float)
			{
				return cn;
			}
		}
		else if(pr_immediate_type == &type_vector)
		{
			if((G_FLOAT(cn->ofs) == pr_immediate.vector[0])
				&& (G_FLOAT(cn->ofs+1) == pr_immediate.vector[1])
				&& (G_FLOAT(cn->ofs+2) == pr_immediate.vector[2]))
			{
				return cn;
			}
		}
		else
		{
			MS_ParseError("weird immediate type");
		}
	}

	if(hcc_OptimizeImmediates)
	{ // Check for a constant with the same value
		for(cn = pr.def_head.next; cn; cn = cn->next)
		{
			if(!cn->initialized || (cn->type != pr_immediate_type))
			{
				continue;
			}
			if(pr_immediate_type == &type_string)
			{
				if(!STRCMP(G_STRING(cn->ofs), pr_immediate_string))
				{
					cell = malloc(sizeof(struct hash_element));
					cell->next = HashTable[index];
					cell->def = cn;
					HashTable[index] = cell;
					return cn;
				}
			}
			else if(pr_immediate_type == &type_float)
			{
				if(G_FLOAT(cn->ofs) == pr_immediate._float)
				{
					cell = malloc(sizeof(struct hash_element));
					cell->next = HashTable[index];
					cell->def = cn;
					HashTable[index] = cell;
					return cn;
				}
			}
			else if(pr_immediate_type == &type_vector)
			{
				if((G_FLOAT(cn->ofs) == pr_immediate.vector[0])
					&& ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1])
					&& ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2]))
				{
					cell = malloc(sizeof(struct hash_element));
					cell->next = HashTable[index];
					cell->def = cn;
					HashTable[index] = cell;
					return cn;
				}
			}
			else
			{
				MS_ParseError("weird immediate type");
			}
		}
	}

	// Allocate a new one
	cn = malloc(sizeof(def_t));
	memset(cn, 0, sizeof(*cn));
	cn->next = NULL;
	pr.def_tail->next = cn;
	pr.def_tail = cn;

	cell = malloc(sizeof(struct hash_element));
	cell->next = HashTable[index];
	cell->def = cn;
	HashTable[index] = cell;

	cn->type = pr_immediate_type;
	//cn->name = "IMMEDIATE";
	cn->name = "I+";
	cn->initialized = 1;
	cn->scope = NULL; // Always share immediates

	// Copy the immediate to the global area
	cn->ofs = numpr_globals;
	pr_global_defs[cn->ofs] = cn;
	numpr_globals += type_size[pr_immediate_type->type];
	if(pr_immediate_type == &type_string)
	{
		pr_immediate.string = CopyString(pr_immediate_string);
	}
	memcpy(pr_globals+cn->ofs, &pr_immediate,
		4*type_size[pr_immediate_type->type]);

	return cn;
}

//==========================================================================
//
// ParseState
//
// [function]
// [$frame function]
// [++ $frame1..$frame2]
// [-- $frame2..$frame1]
// [++(w) $frame1..$frame2]
// [--(w) $frame2..$frame1]
//
//==========================================================================

static void ParseState(void)
{
	char *name;
	def_t *def;
	def_t *s1, *s2;
	int frame1, frame2;
	int direction;
	qboolean weapon;

	if(pr_token_type == tt_name)
	{
		FrameIndex++;

		// Stuff a float for CO_ParseImmediate()
		pr_token_type = tt_immediate;
		pr_immediate_type = &type_float;
		pr_immediate._float = (float)FrameIndex;
		s1 = CO_ParseImmediate();

		pr_token_type = tt_name;
		name = PR_ParseName();
		def = PR_GetDef(&type_function, name, 0, true);
		LX_Require("]");
		CO_GenCode(&pr_opcodes[OP_STATE], s1, def);
		return;
	}
	else if(pr_tokenclass == TK_INC || pr_tokenclass == TK_DEC)
	{
		direction = pr_tokenclass;
		weapon = false;
		LX_Fetch();
		if(TK_CHECK(TK_LPAREN))
		{
			if(LX_Check("w"))
			{
				LX_Fetch();
			}
			else
			{
				LX_Require("W");
			}
			LX_Require(")");
			weapon = true;
		}
		if(pr_token_type != tt_immediate
			|| pr_immediate_type != &type_float)
		{
			MS_ParseError("state frame must be a number");
		}
		frame1 = (int)pr_immediate._float;
		s1 = CO_ParseImmediate();
		LX_Fetch();
		LX_Require("..");
		if(pr_token_type != tt_immediate
			|| pr_immediate_type != &type_float)
		{
			MS_ParseError("state frame must be a number");
		}
		frame2 = (int)pr_immediate._float;
		s2 = CO_ParseImmediate();
		LX_Fetch();
		if(direction == TK_INC)
		{
			if(frame1 > frame2)
			{
				MS_ParseError("bad frame order in state cycle");
			}
		}
		else if(frame1 < frame2)
		{
			MS_ParseError("bad frame order in state cycle");
		}
		LX_Require("]");
		if(weapon == true)
		{
			CO_GenCode(&pr_opcodes[OP_CWSTATE], s1, s2);
		}
		else
		{
			CO_GenCode(&pr_opcodes[OP_CSTATE], s1, s2);
		}
		return;
	}

	if(pr_token_type != tt_immediate
		|| pr_immediate_type != &type_float)
	{
		MS_ParseError("state frame must be a number");
	}
	FrameIndex = (int)pr_immediate._float;
	s1 = CO_ParseImmediate();
	LX_Fetch();
	if(pr_tokenclass == TK_COMMA)
	{
		LX_Fetch();
	}
	name = PR_ParseName();
	def = PR_GetDef(&type_function, name, 0, true);

	LX_Require("]");
	CO_GenCode(&pr_opcodes[OP_STATE], s1, def);
}

//==========================================================================
//
// ParseImmediateStatements
//
//==========================================================================

static function_t *ParseImmediateStatements(type_t *type)
{
	int i;
	function_t *f;
	def_t *defs[MAX_PARMS];
	def_t *scopeDef;
	def_t *searchDef;

	f = malloc(sizeof(function_t));

	// Check for builtin function definition
	if(TK_CHECK(TK_COLON))
	{
		if(pr_token_type != tt_immediate
			|| pr_immediate_type != &type_float
			|| pr_immediate._float != (int)pr_immediate._float)
		{
			MS_ParseError("bad builtin immediate");
		}
		f->builtin = (int)pr_immediate._float;
		LX_Fetch();
		return f;
	}

	f->builtin = 0;

	// Define the parms
	for(i = 0; i < type->num_parms; i++)
	{
		defs[i] = PR_GetDef(type->parm_types[i], pr_parm_names[i],
			pr_scope, true);
		f->parm_ofs[i] = defs[i]->ofs;
		if(i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1])
		{
			MS_ParseError("bad parm order");
		}
	}

	f->code = numstatements;

	// Check for a state opcode
	if(TK_CHECK(TK_LBRACKET))
	{
		ParseState();
	}

	// Check for regular statements
	st_ReturnType = type->aux_type;
	st_ReturnParsed = false;
	if(TK_CHECK(TK_LBRACE))
	{
		scopeDef = pr_scope;
		searchDef = pr.def_tail;

		while(pr_tokenclass != TK_RBRACE)
		{
			ST_ParseStatement();
		}

		while((searchDef = searchDef->next) != NULL)
		{
			if(searchDef->scope == scopeDef
				&& searchDef->referenceCount == 0
				&& searchDef->parentVector == NULL)
			{
				MS_ParseWarning("unreferenced local variable '%s'",
					searchDef->name);
			}
		}
		if(type->aux_type->type != ev_void && st_ReturnParsed == false)
		{
			MS_ParseError("missing return");
		}

		LX_Fetch();
	}
	else if(type->aux_type->type != ev_void && st_ReturnParsed == false)
	{
		MS_ParseError("missing return");
	}

	// Emit an end of statements opcode
	CO_GenCode(pr_opcodes, 0, 0);

	return f;
}

//==========================================================================
//
// PR_GetDef
//
// If type is NULL, it will match any type.  If allocate is true, a new
// def will be allocated if it can't be found.
//
//==========================================================================

def_t *PR_GetDef(type_t *type, char *name, def_t *scope,
	qboolean allocate)
{
	def_t *def;
	def_t *elementDef;
	char element[MAX_NAME];

	char key[100];
	int index = 0;
	struct hash_element *cell = NULL;

	if(scope != NULL)
	{
		sprintf(key, "%s:%s", name, scope->name);
		index = MS_Hash(key);
	}
	else
	{
		index = MS_Hash(name);
	}

	for(cell = HashTable[index]; cell != NULL; cell = cell->next)
	{
		def = cell->def;
		if(!STRCMP(def->name, name))
		{ // Found a match
			if(def->scope && def->scope != scope)
			{ // In a different function
				continue;
			}
			if(type && def->type != type)
			{ // Type mismatch
				MS_ParseError("type mismatch on redeclaration of %s", name);
			}
			return def;
		}
	}

	if(scope != NULL)
	{
		index = MS_Hash(name);
		for(cell = HashTable[index]; cell != NULL; cell = cell->next)
		{
			def = cell->def;
			if(!STRCMP(def->name, name))
			{ // Found a match
				if(def->scope && def->scope != scope)
				{ // In a different function
					continue;
				}
				if(type && def->type != type)
				{ // Type mismatch
					MS_ParseError("type mismatch on redeclaration of %s",
						name);
				}
				return def;
			}
		}
	}

	if(allocate == false)
	{
		return NULL;
	}

	// Allocate a new def
	def = malloc(sizeof(def_t));
	memset(def, 0, sizeof(*def));
	def->next = NULL;
	pr.def_tail->next = def;
	pr.def_tail = def;

	// Add to hash table
	cell = malloc(sizeof(struct hash_element));
	cell->next = HashTable[index];
	cell->def = def;
	HashTable[index] = cell;

	def->name = malloc(strlen(name)+1);
	strcpy(def->name, name);
	def->type = type;

	def->scope = scope;

	def->ofs = numpr_globals;
	pr_global_defs[numpr_globals] = def;

	if(type->type == ev_vector)
	{ // Create vector element defs
		sprintf(element, "%s_x", name);
		elementDef = PR_GetDef(&type_float, element, scope, true);
		elementDef->parentVector = def;
		sprintf(element, "%s_y", name);
		elementDef = PR_GetDef(&type_float, element, scope, true);
		elementDef->parentVector = def;
		sprintf(element, "%s_z", name);
		elementDef = PR_GetDef(&type_float, element, scope, true);
		elementDef->parentVector = def;
	}
	else
	{
		numpr_globals += type_size[type->type];
	}

	if(type->type == ev_field)
	{
		*(int *)&pr_globals[def->ofs] = pr.size_fields;
		if(type->aux_type->type == ev_vector)
		{ // Create vector element defs
			sprintf(element, "%s_x", name);
			PR_GetDef(&type_floatfield, element, scope, true);
			sprintf(element, "%s_y", name);
			PR_GetDef(&type_floatfield, element, scope, true);
			sprintf(element, "%s_z", name);
			PR_GetDef(&type_floatfield, element, scope, true);
		}
		else
		{
			pr.size_fields += type_size[type->aux_type->type];
		}
	}

	return def;
}

//==========================================================================
//
// CO_ParseDefs
//
//==========================================================================

void CO_ParseDefs(void)
{
	int i;
	char *name;
	def_t *def;
	type_t *type;
	int elementCount;
	gofs_t offset;

	type = PR_ParseType();

	if(type == &type_union)
	{
		if(pr_scope)
		{
			MS_ParseError("unions must be global");
		}
		ParseUnion();
		return;
	}

	if(pr_scope && (type->type == ev_field || type->type == ev_function))
	{
		MS_ParseError("fields and functions must be global");
	}

	do
	{
		name = PR_ParseName();
		if(TK_CHECK(TK_LPAREN))
		{
			ParseCStyleFunctionDef(name, type);
			return;
		}

		elementCount = 0;
		if(TK_CHECK(TK_LBRACKET))
		{
			def = GetArrayDef(name, type, &elementCount);
		}
		else
		{
			def = PR_GetDef(type, name, pr_scope, true);
		}

		// Check for initialization
		if(TK_CHECK(TK_ASSIGN))
		{
			if(def->initialized)
			{
				MS_ParseError("'%s' : redefinition", name);
			}
			if(type->type == ev_field)
			{
				MS_ParseError("fields cannot be initialized");
			}

			if(elementCount != 0)
			{
				LX_Require("{");
				i = 0;
				offset = def->ofs;
				do
				{
					if(pr_token_type != tt_immediate)
					{
						MS_ParseError("immediate type required for %s",
							name);
					}
					if(pr_immediate_type->type != type->type)
					{
						MS_ParseError("wrong immediate type for %s", name);
					}
					memcpy(pr_globals+offset, &pr_immediate,
						4*type_size[pr_immediate_type->type]);
					offset += type_size[pr_immediate_type->type];
					i++;
					LX_Fetch();
				} while(TK_CHECK(TK_COMMA));
				LX_Require("}");
				if(i != elementCount)
				{
					MS_ParseError("element count mismatch in array"
						" initialization");
				}
				def->initialized = 1;
				continue;
			}
			else if(type->type == ev_function)
			{
				ParseFunctionDef(def, type);
				continue;
			}
			else if(pr_immediate_type != type)
			{
				MS_ParseError("wrong immediate type for %s", name);
			}
			def->initialized = 1;
			memcpy(pr_globals+def->ofs, &pr_immediate,
				4*type_size[pr_immediate_type->type]);
			LX_Fetch();
		}
		else if(elementCount != 0 && type->type != ev_field)
		{
			memset(pr_globals+def->ofs, 0,
				elementCount*4*type_size[type->type]);
			def->initialized = 1;
		}
	} while(TK_CHECK(TK_COMMA));
	LX_Require(";");
}

//==========================================================================
//
// GetArrayDef
//
//==========================================================================

static def_t *GetArrayDef(char *name, type_t *type, int *elementCount)
{
	def_t *def;
	int count;
	int regCount;

	if(type->type == ev_field)
	{
		return GetFieldArrayDef(name, type, elementCount);
	}

	count = ParseArray(name, type);

	def = NewVarDef(name, type);

	// Precede the array register storage with the element count
	// for run-time boundary checking.
	G_INT(numpr_globals) = count-1;
	numpr_globals++;

	def->ofs = numpr_globals;

	regCount = count*type_size[type->type];
	do
	{
		pr_global_defs[numpr_globals] = def;
		numpr_globals++;
	} while(--regCount);

	*elementCount = count;
	return def;
}

//==========================================================================
//
// GetFieldArrayDef
//
//==========================================================================

static def_t *GetFieldArrayDef(char *name, type_t *type,
	int *elementCount)
{
	def_t *def;
	int count;

	count = ParseArray(name, type);

	def = NewVarDef(name, type);

	def->ofs = numpr_globals;
	pr_global_defs[numpr_globals] = def;
	G_INT(numpr_globals) = pr.size_fields;
	numpr_globals++;
	pr.size_fields += type_size[type->aux_type->type]*count;

	*elementCount = count;
	return def;
}

//==========================================================================
//
// NewVarDef
//
//==========================================================================

static def_t *NewVarDef(char *name, type_t *type)
{
	def_t *def;
	int index;
	struct hash_element *cell;
	char key[100];

	// Allocate the array def
	def = malloc(sizeof(def_t));
	memset(def, 0, sizeof(*def));
	def->next = NULL;
	pr.def_tail->next = def;
	pr.def_tail = def;

	// Add it to the hash table
	if(pr_scope != NULL)
	{
		sprintf(key, "%s:%s", name, pr_scope->name);
		index = MS_Hash(key);
	}
	else
	{
		index = MS_Hash(name);
	}
	cell = malloc(sizeof(struct hash_element));
	cell->next = HashTable[index];
	cell->def = def;
	HashTable[index] = cell;

	def->name = malloc(strlen(name)+1);
	strcpy(def->name, name);

	def->type = type;
	def->scope = pr_scope;

	return def;
}

//==========================================================================
//
// ParseArray
//
//==========================================================================

static int ParseArray(char *name, type_t *type)
{
	int count;
	etype_t t;

	t = type->type;
	if(t == ev_field)
	{
		t = type->aux_type->type;
	}
	if(t != ev_float && t != ev_vector && t != ev_string
		&& t != ev_entity && t != ev_function)
	{
		MS_ParseError("bad array type");
	}
	if(PR_GetDef(type, name, pr_scope, false) != NULL)
	{
		MS_ParseError("array redefinition");
	}
	if(pr_token_type != tt_immediate
		|| pr_immediate_type != &type_float
		|| pr_immediate._float != (int)pr_immediate._float)
	{
		MS_ParseError("subscript is not integral");
	}
	count = (int)pr_immediate._float;
	if(count < 1 || count > MAX_ARRAY_ELEMENTS)
	{
		MS_ParseError("bad subscript: %d", count);
	}
	LX_Fetch();
	LX_Require("]");
	return count;
}

//==========================================================================
//
// ParseUnion
//
//==========================================================================

static void ParseUnion(void)
{
	char *name;
	type_t *type;
	int startFieldOffset;
	int highestFieldOffset;

	LX_Require("{");
	startFieldOffset = pr.size_fields;
	highestFieldOffset = startFieldOffset;
	do
	{
		if(LX_CheckFetch("struct"))
		{
			pr.size_fields = startFieldOffset;
			LX_Require("{");
			do
			{
				type = ParseUnionType();
				do
				{
					name = PR_ParseName();
					PR_GetDef(type, name, pr_scope, true);
				} while(TK_CHECK(TK_COMMA));
				LX_Require(";");
			} while(pr_tokenclass != TK_RBRACE);
			LX_Require("}");
			LX_Require(";");
			if(pr.size_fields > highestFieldOffset)
			{
				highestFieldOffset = pr.size_fields;
			}
		}
		else
		{
			type = ParseUnionType();
			do
			{
				name = PR_ParseName();
				pr.size_fields = startFieldOffset;
				PR_GetDef(type, name, pr_scope, true);
				if(pr.size_fields > highestFieldOffset)
				{
					highestFieldOffset = pr.size_fields;
				}
			} while(TK_CHECK(TK_COMMA));
			LX_Require(";");
		}
	} while(pr_tokenclass != TK_RBRACE);
	LX_Require("}");
	LX_Require(";");
	pr.size_fields = highestFieldOffset;
}

//==========================================================================
//
// ParseUnionType
//
//==========================================================================

static type_t *ParseUnionType(void)
{
	type_t *type;
	type_t newType;

	type = PR_ParseType();
	if(type->type == ev_field)
	{
		MS_ParseError("union field types are implicit");
	}
	memset(&newType, 0, sizeof(newType));
	newType.type = ev_field;
	newType.aux_type = type;
	return PR_FindType(&newType);
}

//==========================================================================
//
// ParseFunctionDef
//
//==========================================================================

static void ParseFunctionDef(def_t *def, type_t *type)
{
	int i;
	function_t *f;
	dfunction_t *df;
	int locals_start;

	locals_start = locals_end = numpr_globals;
	pr_scope = def;
	f = ParseImmediateStatements(type);
	pr_scope = NULL;
	def->initialized = 1;
	G_FUNCTION(def->ofs) = numfunctions;
	f->def = def;

	// Fill in the dfunction
	df = &functions[numfunctions];
	numfunctions++;
	if(f->builtin)
	{
		df->first_statement = -f->builtin;
		def->referenceCount++;	
	}
	else
	{
		df->first_statement = f->code;
	}
	df->s_name = CopyString(f->def->name);
	df->s_file = s_file;
	df->numparms =  f->def->type->num_parms;
	df->locals = locals_end-locals_start;
	df->parm_start = locals_start;
	for(i = 0; i < df->numparms; i++)
	{
		df->parm_size[i] = type_size[f->def->type->parm_types[i]->type];
	}
}

//==========================================================================
//
// ParseCStyleFunctionDef
//
//==========================================================================

static void ParseCStyleFunctionDef(char *funcName, type_t *type)
{
	int i;
	char *name;
	type_t newType;
	type_t *funcType;
	def_t *def;
	function_t *f;
	dfunction_t *df;
	int locals_start;
	char funcIdent[MAX_NAME];
	int initClass;

	strcpy(funcIdent, funcName);
	memset(&newType, 0, sizeof(newType));
	newType.type = ev_function;
	newType.aux_type = type; // Return type
	newType.num_parms = 0;
	if(!TK_CHECK(TK_RPAREN))
	{
		if(TK_CHECK(TK_ELLIPSIS))
		{ // Variable args
			newType.num_parms = -1;
		}
		else if(!LX_CheckFetch("void"))
		{
			do
			{
				type = PR_ParseType();
				name = PR_ParseName();
				strcpy(pr_parm_names[newType.num_parms], name);
				newType.parm_types[newType.num_parms] = type;
				newType.num_parms++;
			} while(TK_CHECK(TK_COMMA));
		}
		LX_Require(")");
	}

	funcType = PR_FindType(&newType);
	def = PR_GetDef(funcType, funcIdent, pr_scope, true);

	if(def->initialized)
	{
		MS_ParseError("%s redeclared", funcName);
	}

	if(TK_TEST(TK_LBRACE)
		|| TK_TEST(TK_LBRACKET)
		|| TK_TEST(TK_COLON))
	{
		initClass = pr_tokenclass;
		if(def->initialized)
		{
			MS_ParseError("%s redeclared", name);
		}
		locals_start = locals_end = numpr_globals;
		pr_scope = def;
		f = ParseImmediateStatements(funcType);
		pr_scope = NULL;
		def->initialized = 1;
		G_FUNCTION(def->ofs) = numfunctions;
		f->def = def;
		df = &functions[numfunctions];
		numfunctions++;
		if(f->builtin)
		{
			df->first_statement = -f->builtin;
			def->referenceCount++;
		}
		else
		{
			df->first_statement = f->code;
		}
		df->s_name = CopyString(f->def->name);
		df->s_file = s_file;
		df->numparms =  f->def->type->num_parms;
		df->locals = locals_end - locals_start;
		df->parm_start = locals_start;
		for(i = 0; i < df->numparms; i++)
		{
			df->parm_size[i] =
				type_size[f->def->type->parm_types[i]->type];
		}
		if(initClass == TK_COLON)
		{
			LX_Require(";");
		}
	}
	else
	{
		LX_Require(";");
	}
}
