/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2015  Warzone 2100 Project

	Warzone 2100 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.

	Warzone 2100 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 Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
%defines
%{
/*
 * script.y
 *
 * The yacc grammar for the scipt files.
 */

#include "lib/framework/frame.h"
#include "lib/framework/frameresource.h"
#include "lib/framework/string_ext.h"
#include "lib/script/interpreter.h"
#include "lib/script/parse.h"
#include "lib/script/script.h"

#include <physfs.h>

/* this will give us a more detailed error output */
#define YYERROR_VERBOSE true

/* Script defines stack */

extern int scr_lex(void);
extern int scr_lex_destroy(void);

/* Error return codes for code generation functions */
enum CODE_ERROR
{
	CE_OK,				// No error
	CE_MEMORY,			// Out of memory
	CE_PARSE			// A parse error occured
};

/* Turn off a couple of warnings that the yacc generated code gives */

/* Pointer to the compiled code */
static SCRIPT_CODE	*psFinalProg=NULL;

/* Pointer to current block of compiled code */
static CODE_BLOCK	*psCurrBlock=NULL;

/* Pointer to current block of conditional code */
static COND_BLOCK	*psCondBlock=NULL;

/* Pointer to current block of object variable code */
static OBJVAR_BLOCK	*psObjVarBlock=NULL;

/* Pointer to current block of compiled parameter code */
static PARAM_BLOCK	*psCurrPBlock=NULL;

/* Any errors occured? */
static bool bError=false;

//String support
//-----------------------------
char 				msg[MAXSTRLEN];
extern char			STRSTACK[MAXSTACKLEN][MAXSTRLEN];	// just a simple string "stack"
extern UDWORD		CURSTACKSTR;				//Current string index

/* Pointer into the current code block */
static INTERP_VAL		*ip;

/* Pointer to current parameter declaration block */
//static PARAM_DECL	*psCurrParamDecl=NULL;

/* Pointer to current trigger subdeclaration */
static TRIGGER_DECL	*psCurrTDecl=NULL;

/* Pointer to current variable subdeclaration */
static VAR_DECL		*psCurrVDecl=NULL;

/* Pointer to the current variable identifier declaration */
static VAR_IDENT_DECL	*psCurrVIdentDecl=NULL;

/* Pointer to the current array access block */
static ARRAY_BLOCK		*psCurrArrayBlock=NULL;

/* Return code from code generation functions */
static CODE_ERROR	codeRet;

/* The list of global variables */
static VAR_SYMBOL	*psGlobalVars=NULL;

/* The list of global arrays */
static VAR_SYMBOL	*psGlobalArrays=NULL;

#define			maxEventsLocalVars		1200
static VAR_SYMBOL	*psLocalVarsB[maxEventsLocalVars];	/* local var storage */
static UDWORD		numEventLocalVars[maxEventsLocalVars];	/* number of declard local vars for each event */
EVENT_SYMBOL		*psCurEvent = NULL;		/* stores current event: for local var declaration */

/* The current object variable context */
static INTERP_TYPE	objVarContext = (INTERP_TYPE)0;

/* Control whether debug info is generated */
static bool			genDebugInfo = true;

/* Currently defined triggers */
static TRIGGER_SYMBOL	*psTriggers;
static UDWORD			numTriggers;

/* Currently defined events */
static EVENT_SYMBOL		*psEvents;
static UDWORD			numEvents;

/* This is true when local variables are being defined.
 * (So local variables can have the same name as global ones)
 */
static bool localVariableDef=false;

/* The identifier for the current script function being defined */
//static char *pCurrFuncIdent=NULL;

/* A temporary store for a line number - used when
 * generating debugging info for functions, conditionals and loops.
 */
static UDWORD		debugLine;

/* The table of user types */
TYPE_SYMBOL		*asScrTypeTab;

/* The table of instinct function type definitions */
FUNC_SYMBOL		*asScrInstinctTab;

/* The table of external variables and their access functions */
VAR_SYMBOL		*asScrExternalTab;

/* The table of object variables and their access functions. */
VAR_SYMBOL		*asScrObjectVarTab;

/* The table of constant variables */
CONST_SYMBOL	*asScrConstantTab;

/* The table of callback triggers */
CALLBACK_SYMBOL	*asScrCallbackTab;

/* Used for additional debug output */
void script_debug(const char *pFormat, ...);

/****************************************************************************************
 *
 * Code Block Macros
 *
 * These macros are used to allocate and free the different types of code
 * block used within the compiler
 */


/* What the macro should do if it has an allocation error.
 * This is different depending on whether the macro is used
 * in a function, or in a rule body.
 *
 * This definition is used within the code generation functions
 * and is then changed for use within a rule body.
 */
#define ALLOC_ERROR_ACTION return CE_MEMORY

/* Macro to allocate a program structure, size is in _bytes_ */
#define ALLOC_PROG(psProg, codeSize, pAICode, numGlobs, numArys, numTrigs, numEvnts) \
	(psProg) = (SCRIPT_CODE *)malloc(sizeof(SCRIPT_CODE)); \
	if ((psProg) == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psProg)->pCode = (INTERP_VAL *)malloc((codeSize) * sizeof(INTERP_VAL)); \
	if ((psProg)->pCode == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	if (numGlobs > 0) \
	{ \
		(psProg)->pGlobals = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (numGlobs)); \
		if ((psProg)->pGlobals == NULL) \
		{ \
			debug(LOG_ERROR, "Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
	} \
	else \
	{ \
		(psProg)->pGlobals = NULL; \
	} \
	if (numArys > 0) \
	{ \
		(psProg)->psArrayInfo = (ARRAY_DATA *)malloc(sizeof(ARRAY_DATA) * (numArys)); \
		if ((psProg)->psArrayInfo == NULL) \
		{ \
			debug(LOG_ERROR, "Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
	} \
	else \
	{ \
		(psProg)->psArrayInfo = NULL; \
	} \
	(psProg)->numArrays = (UWORD)(numArys); \
	if ((numTrigs) > 0) \
	{ \
		(psProg)->pTriggerTab = (uint16_t *)malloc(sizeof(uint16_t) * ((numTrigs) + 1)); \
		if ((psProg)->pTriggerTab == NULL) \
		{ \
			debug(LOG_ERROR, "Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
		(psProg)->psTriggerData = (TRIGGER_DATA *)malloc(sizeof(TRIGGER_DATA) * (numTrigs)); \
		if ((psProg)->psTriggerData == NULL) \
		{ \
			debug(LOG_ERROR, "Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
	} \
	else \
	{ \
		(psProg)->pTriggerTab = NULL; \
		(psProg)->psTriggerData = NULL; \
	} \
	(psProg)->pEventTab = (uint16_t *)malloc(sizeof(uint16_t) * ((numEvnts) + 1)); \
	if ((psProg)->pEventTab == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psProg)->pEventLinks = (int16_t *)malloc(sizeof(int16_t) * (numEvnts)); \
	if ((psProg)->pEventLinks == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psProg)->numGlobals = (UWORD)(numGlobs); \
	(psProg)->numTriggers = (UWORD)(numTriggers); \
	(psProg)->numEvents = (UWORD)(numEvnts); \
	(psProg)->size = (codeSize) * sizeof(INTERP_VAL);

/* Macro to allocate a code block, blockSize - number of INTERP_VALs we need*/
#define ALLOC_BLOCK(psBlock, num) \
	(psBlock) = (CODE_BLOCK *)malloc(sizeof(CODE_BLOCK)); \
	if ((psBlock) == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psBlock)->pCode = (INTERP_VAL *)malloc((num) * sizeof(INTERP_VAL)); \
	if ((psBlock)->pCode == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		free((psBlock)); \
		ALLOC_ERROR_ACTION; \
	} \
	(psBlock)->size = (num); \
	(psBlock)->debugEntries = 0

/* Macro to free a code block */
#define FREE_BLOCK(psBlock) \
	free((psBlock)->pCode); \
	free((psBlock))

/* Macro to allocate a parameter block */
#define ALLOC_PBLOCK(psBlock, num, paramSize) \
	(psBlock) = (PARAM_BLOCK *)malloc(sizeof(PARAM_BLOCK)); \
	if ((psBlock) == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psBlock)->pCode = (INTERP_VAL *)malloc((num) * sizeof(INTERP_VAL)); \
	if ((psBlock)->pCode == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		free((psBlock)); \
		ALLOC_ERROR_ACTION; \
	} \
	(psBlock)->aParams = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (paramSize)); \
	if ((psBlock)->aParams == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		free((psBlock)->pCode); \
		free((psBlock)); \
		ALLOC_ERROR_ACTION; \
	} \
	(psBlock)->size = (num); \
	(psBlock)->numParams = (paramSize)

/* Macro to free a parameter block */
#define FREE_PBLOCK(psBlock) \
	free((psBlock)->pCode); \
	free((psBlock)->aParams); \
	free((psBlock))

/* Macro to allocate a parameter declaration block */
#define ALLOC_PARAMDECL(psPDecl, num) \
	(psPDecl) = (PARAM_DECL *)malloc(sizeof(PARAM_DECL)); \
	if ((psPDecl) == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psPDecl)->aParams = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (num)); \
	if ((psPDecl)->aParams == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psPDecl)->numParams = (num)

/* Macro to free a parameter declaration block */
#define FREE_PARAMDECL(psPDecl) \
	free((psPDecl)->aParams); \
	free((psPDecl))

/* Macro to allocate a conditional block */
#define ALLOC_CONDBLOCK(psCB, num, numBlocks) \
	(psCB) = (COND_BLOCK *)malloc(sizeof(COND_BLOCK)); \
	if ((psCB) == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psCB)->aOffsets = (UDWORD *)malloc(sizeof(SDWORD) * (num)); \
	if ((psCB)->aOffsets == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psCB)->pCode = (INTERP_VAL *)malloc((numBlocks) * sizeof(INTERP_VAL)); \
	if ((psCB)->pCode == NULL) \
	{ \
		debug(LOG_ERROR, "Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psCB)->size = (numBlocks); \
	(psCB)->numOffsets = (num)

/* Macro to free a conditional block */
#define FREE_CONDBLOCK(psCB) \
	free((psCB)->aOffsets); \
	free((psCB)->pCode); \
	free(psCB)

/* Macro to free a code block */
#define FREE_USERBLOCK(psBlock) \
	free((psBlock)->pCode); \
	free((psBlock))

/* Macro to allocate an object variable block */
#define ALLOC_OBJVARBLOCK(psOV, blockSize, psVar) \
	(psOV) = (OBJVAR_BLOCK *)malloc(sizeof(OBJVAR_BLOCK)); \
	if ((psOV) == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psOV)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \
	if ((psOV)->pCode == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psOV)->size = (blockSize); \
	(psOV)->psObjVar = (psVar)

/* Macro to free an object variable block */
#define FREE_OBJVARBLOCK(psOV) \
	free((psOV)->pCode); \
	free(psOV)

/* Macro to allocate an array variable block */
#define ALLOC_ARRAYBLOCK(psAV, blockSize, psVar) \
	(psAV) = (ARRAY_BLOCK *)malloc(sizeof(ARRAY_BLOCK)); \
	if ((psAV) == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psAV)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \
	if ((psAV)->pCode == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psAV)->size = (blockSize); \
	(psAV)->dimensions = 1; \
	(psAV)->psArrayVar = (psVar)

/* Macro to free an object variable block */
#define FREE_ARRAYBLOCK(psAV) \
	free((psAV)->pCode); \
	free((psAV))

/* Allocate a trigger subdecl */
#define ALLOC_TSUBDECL(psTSub, blockType, blockSize, blockTime) \
	(psTSub) = (TRIGGER_DECL *)malloc(sizeof(TRIGGER_DECL)); \
	if ((psTSub) == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	} \
	(psTSub)->type = (blockType); \
	(psTSub)->time = (blockTime); \
	if ((blockSize) > 0) \
	{ \
		(psTSub)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \
		if ((psTSub)->pCode == NULL) \
		{ \
			scr_error("Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
		(psTSub)->size = (blockSize); \
	} \
	else \
	{ \
		(psTSub)->pCode = NULL; \
		(psTSub)->size = 0; \
	}

/* Free a trigger subdecl */
#define FREE_TSUBDECL(psTSub) \
	if ((psTSub)->pCode) \
	{ \
		free((psTSub)->pCode); \
	} \
	free(psTSub)

/* Allocate a variable declaration block */
#define ALLOC_VARDECL(psDcl) \
	(psDcl) = (VAR_DECL *)malloc(sizeof(VAR_DECL)); \
	if ((psDcl) == NULL) \
	{ \
		scr_error("Out of memory"); \
		ALLOC_ERROR_ACTION; \
	}

/* Free a variable declaration block */
#define FREE_VARDECL(psDcl) \
	free(psDcl)

/* Allocate a variable declaration block */
static inline CODE_ERROR do_ALLOC_VARIDENTDECL(VAR_IDENT_DECL** psDcl, const char* ident, unsigned int dim)
{
	// Allocate memory
	*psDcl = (VAR_IDENT_DECL *)malloc(sizeof(VAR_IDENT_DECL));
	if (*psDcl == NULL)
	{
		scr_error("Out of memory");
		ALLOC_ERROR_ACTION;
	}

	// Copy over the "ident" string (if it's there)
	if (ident != NULL)
	{
		(*psDcl)->pIdent = strdup(ident);
		if ((*psDcl)->pIdent == NULL)
		{
			scr_error("Out of memory");
			ALLOC_ERROR_ACTION;
		}
	}
	else
	{
		(*psDcl)->pIdent = NULL;
	}

	(*psDcl)->dimensions = dim;

	return CE_OK;
}

#define ALLOC_VARIDENTDECL(psDcl, ident, dim) \
{ \
	CODE_ERROR err = do_ALLOC_VARIDENTDECL(&psDcl, ident, dim); \
	if (err != CE_OK) \
		return err; \
}

/* Free a variable declaration block */
static void freeVARIDENTDECL(VAR_IDENT_DECL* psDcl)
{
	free(psDcl->pIdent);
	free(psDcl);
}

/****************************************************************************************
 *
 * Code block manipulation macros.
 *
 * These are used to copy chunks of code from one block to another
 * or to insert opcodes or other values into a code block.
 * All the macros use the ip parameter.  This is a pointer into the code
 * block that is incremented by the macro. This ensures that it always points
 * to the next free space in the code block.
 */

/* Macro to store an opcode in a code block, opcode type is '-1' */
#define PUT_OPCODE(ip, opcode) \
	(ip)->type = VAL_OPCODE; \
	(ip)->v.ival = (opcode) << OPCODE_SHIFT; \
	(ip)++

/* Macro to put a packed opcode in a code block */
#define PUT_PKOPCODE(ip, opcode, data) \
	(ip)->type = VAL_PKOPCODE; \
	(ip)->v.ival = ((SDWORD)(data)) & OPCODE_DATAMASK; \
	(ip)->v.ival = (((SDWORD)(opcode)) << OPCODE_SHIFT) | ((ip)->v.ival); \
	(ip)++

/* Special macro for floats, could be a bit tricky */
#define PUT_DATA_FLOAT(ip, data) \
	ip->type = VAL_FLOAT; \
	ip->v.fval = (float)(data); \
	ip++

/* Macros to store a value in a code block */
#define PUT_DATA_BOOL(ip, value) \
	(ip)->type = VAL_BOOL; \
	(ip)->v.bval = (int32_t)(value); \
	(ip)++

#define PUT_DATA_INT(ip, value) \
	(ip)->type = VAL_INT; \
	(ip)->v.ival = (SDWORD)(value); \
	(ip)++

#define PUT_DATA_STRING(ip, value) \
	(ip)->type = VAL_STRING; \
	(ip)->v.sval = (char *)(value); \
	(ip)++

#define PUT_OBJECT_CONST(ip, value, constType) \
	(ip)->type = (constType); \
	(ip)->v.oval = (value); \
	(ip)++

#define PUT_STRING(ip, value) \
	((INTERP_VAL *)(ip))->type = VAL_STRING; \
	((INTERP_VAL *)(ip))->v.sval = (value); \
	((INTERP_VAL *)(ip)) += 1

/* Macro to store an external function index in a code block */
#define PUT_FUNC_EXTERN(ip, func) \
	(ip)->type = VAL_FUNC_EXTERN; \
	(ip)->v.pFuncExtern = (SCRIPT_FUNC)func; \
	(ip)++

/* Macro to store an internal (in-script, actually an event) function index in a code block */
#define PUT_EVENT(ip, func) \
	(ip)->type = VAL_EVENT; \
	(ip)->v.ival = (SDWORD)func; \
	(ip)++

/* Macro to store trigger */
#define PUT_TRIGGER(ip, func) \
	(ip)->type = VAL_TRIGGER; \
	(ip)->v.ival = (SDWORD)func; \
	(ip)++

/* Macro to store a function pointer in a code block */
#define PUT_VARFUNC(ip, func) \
	(ip)->type = VAL_OBJ_GETSET; \
	(ip)->v.pObjGetSet = (SCRIPT_VARFUNC)func; \
	(ip)++

/* Macro to store a variable index number in a code block - NOT USED */
#define PUT_INDEX(ip, index) \
	(ip)->type = -1; \
	(ip)->v.ival = (SDWORD)(index); \
	(ip)++

/* Macro to copy a code block into another code block */
#define PUT_BLOCK(ip, psOldBlock) \
	memcpy(ip, (psOldBlock)->pCode, (psOldBlock)->size * sizeof(INTERP_VAL)); \
	(ip) = (INTERP_VAL *)( (ip) + (psOldBlock)->size)

/***********************************************************************************
 *
 * Debugging information macros
 *
 * These macros are only used to generate debugging information for scripts.
 */

/* Macro to allocate debugging info for a CODE_BLOCK or a COND_BLOCK */
#define ALLOC_DEBUG(psBlock, num) \
	if (genDebugInfo) \
	{ \
		(psBlock)->psDebug = (SCRIPT_DEBUG *)malloc(sizeof(SCRIPT_DEBUG) * (num)); \
		if ((psBlock)->psDebug == NULL) \
		{ \
			scr_error("Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
		memset((psBlock)->psDebug, 0, sizeof(SCRIPT_DEBUG) * (num));\
		(psBlock)->debugEntries = (UWORD)(num); \
	} \
	else \
	{ \
		(psBlock)->psDebug = NULL; \
		(psBlock)->debugEntries = 0; \
	}

/* Macro to free debugging info */
#define FREE_DEBUG(psBlock) \
	if (genDebugInfo) \
		free((psBlock)->psDebug)


/* Macro to copy the debugging information from one block to another */
#define PUT_DEBUG(psFinal, psBlock) \
	if (genDebugInfo) \
	{ \
		memcpy((psFinal)->psDebug, (psBlock)->psDebug, \
					sizeof(SCRIPT_DEBUG) * (psBlock)->debugEntries); \
		(psFinal)->debugEntries = (psBlock)->debugEntries; \
	}

/* Macro to combine the debugging information in two blocks into a third block */
static UDWORD		_dbEntry;
static SCRIPT_DEBUG	*_psCurr;
#define COMBINE_DEBUG(psFinal, psBlock1, psBlock2) \
	if (genDebugInfo) \
	{ \
		memcpy((psFinal)->psDebug, (psBlock1)->psDebug, \
				 sizeof(SCRIPT_DEBUG) * (psBlock1)->debugEntries); \
		_baseOffset = (psBlock1)->size / sizeof(UDWORD); \
		for(_dbEntry = 0; _dbEntry < (psBlock2)->debugEntries; _dbEntry++) \
		{ \
			_psCurr = (psFinal)->psDebug + (psBlock1)->debugEntries + _dbEntry; \
			_psCurr->line = (psBlock2)->psDebug[_dbEntry].line; \
			_psCurr->offset = (psBlock2)->psDebug[_dbEntry].offset + _baseOffset; \
		} \
		(psFinal)->debugEntries = (psBlock1)->debugEntries + (psBlock2)->debugEntries; \
	}

/* Macro to append some debugging information onto a block, given the instruction
   offset of the debugging information already in the destination block */
#define APPEND_DEBUG(psFinal, baseOffset, psBlock) \
	if (genDebugInfo) \
	{ \
		for(_dbEntry = 0; _dbEntry < (psBlock)->debugEntries; _dbEntry++) \
		{ \
			_psCurr = (psFinal)->psDebug + (psFinal)->debugEntries + _dbEntry; \
			_psCurr->line = (psBlock)->psDebug[_dbEntry].line; \
			_psCurr->offset = (psBlock)->psDebug[_dbEntry].offset + (baseOffset); \
		} \
		(psFinal)->debugEntries = (UWORD)((psFinal)->debugEntries + (psBlock)->debugEntries); \
	}


/* Macro to store a label in the debug info */
#define DEBUG_LABEL(psBlock, offset, pString) \
	if (genDebugInfo) \
	{ \
		(psBlock)->psDebug[offset].pLabel = (char *)malloc(strlen(pString)+1); \
		if (!(psBlock)->psDebug[offset].pLabel) \
		{ \
			scr_error("Out of memory"); \
			ALLOC_ERROR_ACTION; \
		} \
		strcpy((psBlock)->psDebug[offset].pLabel, pString); \
	}


/***************************************************************************************
 *
 * Code generation functions
 *
 * These functions are used within rule bodies to generate code.
 */

/* Macro to deal with the errors returned by code generation functions.
 * Used within the rule body.
 */
#define CHECK_CODE_ERROR(error) \
	if ((error) == CE_MEMORY) \
	{ \
		YYABORT; \
	} \
	else if ((error) == CE_PARSE) \
	{ \
		YYERROR; \
	}

void script_debug(const char *pFormat, ...)
{
	char    buffer[500];
	va_list pArgs;

	va_start(pArgs, pFormat);
	vsnprintf(buffer, sizeof(buffer), pFormat, pArgs);
	va_end(pArgs);

	debug(LOG_SCRIPT, "%s", buffer);
}

/* Generate the code for a function call, checking the parameter
 * types match.
 */
static CODE_ERROR scriptCodeFunction(FUNC_SYMBOL	*psFSymbol,		// The function being called
							PARAM_BLOCK		*psPBlock,		// The functions parameters
							int32_t			expContext,		// Whether the function is being
															// called in an expression context
							CODE_BLOCK		**ppsCBlock)	// The generated code block
{
	UDWORD		size, i;
	INTERP_VAL	*ip;
	bool		typeError = false;
	char		aErrorString[255];
	INTERP_TYPE	type1,type2;

	ASSERT( psFSymbol != NULL, "ais_CodeFunction: Invalid function symbol pointer" );
	ASSERT( psPBlock != NULL,
		"scriptCodeFunction: Invalid param block pointer" );
	ASSERT( (psPBlock->size == 0) || psPBlock->pCode != NULL,
		"scriptCodeFunction: Invalid parameter code pointer" );
	ASSERT( ppsCBlock != NULL,
		 "scriptCodeFunction: Invalid generated code block pointer" );

	/* Check the parameter types match what the function needs */
	for(i=0; (i<psFSymbol->numParams) && (i<psPBlock->numParams); i++)
	{
/*		if (psFSymbol->aParams[i] != VAL_VOID &&
			psFSymbol->aParams[i] != psPBlock->aParams[i])*/
		//TODO: string support
		if(psFSymbol->aParams[i] != VAL_STRING)	// string - allow mixed types if string is parameter type	//TODO: tweak this
		{
			type1 = (INTERP_TYPE)psFSymbol->aParams[i];
			type2 = (INTERP_TYPE)psPBlock->aParams[i];
			if (!interpCheckEquiv(type1, type2))
			{
				debug(LOG_ERROR, "scriptCodeFunction: Type mismatch for parameter %d (%d/%d)", i, psFSymbol->aParams[i], psPBlock->aParams[i]);
				snprintf(aErrorString, sizeof(aErrorString), "Type mismatch for parameter %d", i);
				scr_error("%s", aErrorString);
				typeError = true;
			}
		}
		//else
		//{
		//	debug(LOG_SCRIPT, "scriptCodeFunction: %s takes string as parameter %d (provided: %d)", psFSymbol->pIdent, i, psPBlock->aParams[i]);
		//}
	}

	/* Check the number of parameters matches that expected */
	if (psFSymbol->numParams != psPBlock->numParams)
	{
		snprintf(aErrorString, sizeof(aErrorString), "Expected %d parameters", psFSymbol->numParams);
		scr_error("%s", aErrorString);
		*ppsCBlock = NULL;
		return CE_PARSE;
	}

	if (typeError)
	{
		/* Report the error here so all the */
		/* type mismatches are reported */
		*ppsCBlock = NULL;
		return CE_PARSE;
	}

	//size = psPBlock->size + sizeof(OPCODE) + sizeof(SCRIPT_FUNC);
	size = psPBlock->size + 1 + 1;	//size + opcode + sizeof(SCRIPT_FUNC)

	if (!expContext && (psFSymbol->type != VAL_VOID))
	{
		//size += sizeof(OPCODE);
		size += 1;	//+ 1 additional opcode to pop value if needed
	}

	ALLOC_BLOCK(*ppsCBlock, size);
	ip = (*ppsCBlock)->pCode;
	(*ppsCBlock)->type = psFSymbol->type;

	/* Copy in the code for the parameters */
	PUT_BLOCK(ip, psPBlock);
	FREE_PBLOCK(psPBlock);

	/* Make the function call */
	if (psFSymbol->script)
	{
		/* function defined in this script */
//		PUT_OPCODE(ip, OP_FUNC);
//		PUT_SCRIPTFUNC(ip, psFSymbol);
		ASSERT(false, "wrong function type call");
	}
	else
	{
		/* call an instinct function */
		PUT_OPCODE(ip, OP_CALL);
		PUT_FUNC_EXTERN(ip, psFSymbol->pFunc);
	}

	if (!expContext && (psFSymbol->type != VAL_VOID))
	{
		/* Clear the return value from the stack */
		PUT_OPCODE(ip, OP_POP);
	}

	return CE_OK;
}


/* Function call: Check the parameter types match, assumes param count matched */
static UDWORD checkFuncParamTypes(EVENT_SYMBOL		*psFSymbol,		// The function being called
							PARAM_BLOCK		*psPBlock)	// The generated code block
{
	UDWORD		i;

	//debug(LOG_SCRIPT,"checkFuncParamTypes");

	/* Check the parameter types match what the function needs */
	for(i=0; (i<psFSymbol->numParams) && (i<psPBlock->numParams); i++)
	{
		//TODO: string support
		//if(psFSymbol->aParams[i] != VAL_STRING)	// string - allow mixed types if string is parameter type
		//{
			if (!interpCheckEquiv(psFSymbol->aParams[i], psPBlock->aParams[i]))
			{
				debug(LOG_ERROR, "checkFuncParamTypes: Type mismatch for parameter %d ('1' based) in Function '%s' (provided type: %d, expected: %d)", (i+1), psFSymbol->pIdent, psPBlock->aParams[i], psFSymbol->aParams[i]);
				scr_error("Parameter type mismatch");
				return i+1;
			}
		//}
	}

	//debug(LOG_SCRIPT,"END checkFuncParamTypes");

	return 0;	//all ok
}

/* Generate the code for a parameter callback, checking the parameter
 * types match.
 */
static CODE_ERROR scriptCodeCallbackParams(
							CALLBACK_SYMBOL	*psCBSymbol,	// The callback being called
							PARAM_BLOCK		*psPBlock,		// The callbacks parameters
							TRIGGER_DECL	**ppsTDecl)		// The generated code block
{
	UDWORD		size, i;
	INTERP_VAL	*ip;
	bool		typeError = false;
	char		aErrorString[255];

	ASSERT( psPBlock != NULL,
		"scriptCodeCallbackParams: Invalid param block pointer" );
	ASSERT( (psPBlock->size == 0) || psPBlock->pCode != NULL,
		"scriptCodeCallbackParams: Invalid parameter code pointer" );
	ASSERT( ppsTDecl != NULL,
		 "scriptCodeCallbackParams: Invalid generated code block pointer" );
	ASSERT( psCBSymbol->pFunc != NULL,
		 "scriptCodeCallbackParams: Expected function pointer for callback symbol" );

	/* Check the parameter types match what the function needs */
	for(i=0; (i<psCBSymbol->numParams) && (i<psPBlock->numParams); i++)
	{
		if (!interpCheckEquiv((INTERP_TYPE)psCBSymbol->aParams[i], psPBlock->aParams[i]))
		{
			snprintf(aErrorString, sizeof(aErrorString), "Type mismatch for parameter %d", i);
			scr_error("%s", aErrorString);
			typeError = true;
		}
	}

	/* Check the number of parameters matches that expected */
	if (psPBlock->numParams == 0)
	{
		scr_error("Expected parameters to callback");
		*ppsTDecl = NULL;
		return CE_PARSE;
	}
	else if (psCBSymbol->numParams != psPBlock->numParams)
	{
		snprintf(aErrorString, sizeof(aErrorString), "Expected %d parameters", psCBSymbol->numParams);
		scr_error("%s", aErrorString);
		*ppsTDecl = NULL;
		return CE_PARSE;
	}
	if (typeError)
	{
		/* Return the error here so all the */
		/* type mismatches are reported */
		*ppsTDecl = NULL;
		return CE_PARSE;
	}

	//size = psPBlock->size + sizeof(OPCODE) + sizeof(SCRIPT_FUNC);
	size = psPBlock->size + 1 + 1;		//size + opcode + SCRIPT_FUNC

	ALLOC_TSUBDECL(*ppsTDecl, psCBSymbol->type, size, 0);
	ip = (*ppsTDecl)->pCode;

	/* Copy in the code for the parameters */
	PUT_BLOCK(ip, psPBlock);
	FREE_PBLOCK(psPBlock);

	/* call the instinct function */
	PUT_OPCODE(ip, OP_CALL);
	PUT_FUNC_EXTERN(ip, psCBSymbol->pFunc);

	return CE_OK;
}


/* Generate code for assigning a value to a variable */
static CODE_ERROR scriptCodeAssignment(VAR_SYMBOL	*psVariable,	// The variable to assign to
							  CODE_BLOCK	*psValue,		// The code for the value to
															// assign
							  CODE_BLOCK	**ppsBlock)		// Generated code
{
	SDWORD		size;

	ASSERT( psVariable != NULL,
		"scriptCodeAssignment: Invalid variable symbol pointer" );
	ASSERT( psValue != NULL,
		"scriptCodeAssignment: Invalid value code block pointer" );
	ASSERT( psValue->pCode != NULL,
		"scriptCodeAssignment: Invalid value code pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeAssignment: Invalid generated code block pointer" );

	//size = psValue->size + sizeof(OPCODE);
	size = psValue->size + 1;		//1 - for assignment opcode

	if (psVariable->storage == ST_EXTERN)
	{
		// Check there is a set function
		if (psVariable->set == NULL)
		{
			scr_error("No set function for external variable");
			return CE_PARSE;
		}

		//size += sizeof(SCRIPT_VARFUNC);
		size += 1;		//1 - for set func pointer
	}
	ALLOC_BLOCK(*ppsBlock, size);
	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the expression */
	PUT_BLOCK(ip, psValue);
	FREE_BLOCK(psValue);

	/* Code to get the value from the stack into the variable */
	switch (psVariable->storage)
	{
	case ST_PUBLIC:
	case ST_PRIVATE:
		PUT_PKOPCODE(ip, OP_POPGLOBAL, psVariable->index);
		break;
	case ST_LOCAL:
		PUT_PKOPCODE(ip, OP_POPLOCAL, psVariable->index);
		break;
	case ST_EXTERN:
		PUT_PKOPCODE(ip, OP_VARCALL, psVariable->index);
		PUT_VARFUNC(ip, psVariable->set);
		break;
	case ST_OBJECT:
		scr_error("Cannot use member variables in this context");
		return CE_PARSE;
		break;
	default:
		scr_error("Unknown storage type");
		return CE_PARSE;
		break;
	}

	return CE_OK;
}


/* Generate code for assigning a value to an object variable */
static CODE_ERROR scriptCodeObjAssignment(OBJVAR_BLOCK	*psVariable,// The variable to assign to
								 CODE_BLOCK		*psValue,	// The code for the value to
															// assign
								 CODE_BLOCK		**ppsBlock)	// Generated code
{
	ASSERT( psVariable != NULL,
		"scriptCodeObjAssignment: Invalid object variable block pointer" );
	ASSERT( psVariable->pCode != NULL,
		"scriptCodeObjAssignment: Invalid object variable code pointer" );
	ASSERT( psVariable->psObjVar != NULL,
		"scriptCodeObjAssignment: Invalid object variable symbol pointer" );
	ASSERT( psValue != NULL,
		"scriptCodeObjAssignment: Invalid value code block pointer" );
	ASSERT( psValue->pCode != NULL,
		"scriptCodeObjAssignment: Invalid value code pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjAssignment: Invalid generated code block pointer" );

	// Check there is an access function for the variable
	if (psVariable->psObjVar->set == NULL)
	{
		scr_error("No set function for object variable");
		return CE_PARSE;
	}

	//ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + sizeof(OPCODE) + sizeof(SCRIPT_VARFUNC));
	ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + 1 + 1);	//size + size + opcode + 'sizeof(SCRIPT_VARFUNC)'

	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the value */
	PUT_BLOCK(ip, psValue);
	FREE_BLOCK(psValue);

	/* Copy in the code for the object */
	PUT_BLOCK(ip, psVariable);

	/* Code to get the value from the stack into the variable */
	PUT_PKOPCODE(ip, OP_VARCALL, psVariable->psObjVar->index);
	PUT_VARFUNC(ip, (psVariable->psObjVar->set));

	/* Free the variable block */
	FREE_OBJVARBLOCK(psVariable);

	return CE_OK;
}


/* Generate code for getting a value from an object variable */
static CODE_ERROR scriptCodeObjGet(OBJVAR_BLOCK	*psVariable,// The variable to get from
						  CODE_BLOCK	**ppsBlock)	// Generated code
{
	ASSERT( psVariable != NULL,
		"scriptCodeObjAssignment: Invalid object variable block pointer" );
	ASSERT( psVariable->pCode != NULL,
		"scriptCodeObjAssignment: Invalid object variable code pointer" );
	ASSERT( psVariable->psObjVar != NULL,
		"scriptCodeObjAssignment: Invalid object variable symbol pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjAssignment: Invalid generated code block pointer" );

	// Check there is an access function for the variable
	if (psVariable->psObjVar->get == NULL)
	{
		scr_error("No get function for object variable");
		return CE_PARSE;
	}

	//ALLOC_BLOCK(*ppsBlock, psVariable->size + sizeof(OPCODE) + sizeof(SCRIPT_VARFUNC));
	ALLOC_BLOCK(*ppsBlock, psVariable->size + 1 + 1);	//size + opcode + SCRIPT_VARFUNC

	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the object */
	PUT_BLOCK(ip, psVariable);
	(*ppsBlock)->type = psVariable->psObjVar->type;

	/* Code to get the value from the object onto the stack */
	PUT_PKOPCODE(ip, OP_VARCALL, psVariable->psObjVar->index);
	PUT_VARFUNC(ip, psVariable->psObjVar->get);

	/* Free the variable block */
	FREE_OBJVARBLOCK(psVariable);

	return CE_OK;
}


/* Generate code for assigning a value to an array variable */
static CODE_ERROR scriptCodeArrayAssignment(ARRAY_BLOCK	*psVariable,// The variable to assign to
								 CODE_BLOCK		*psValue,	// The code for the value to
															// assign
								 CODE_BLOCK		**ppsBlock)	// Generated code
{
	ASSERT( psVariable != NULL,
		"scriptCodeObjAssignment: Invalid object variable block pointer" );
	ASSERT( psVariable->pCode != NULL,
		"scriptCodeObjAssignment: Invalid object variable code pointer" );
	ASSERT( psVariable->psArrayVar != NULL,
		"scriptCodeObjAssignment: Invalid object variable symbol pointer" );
	ASSERT( psValue != NULL,
		"scriptCodeObjAssignment: Invalid value code block pointer" );
	ASSERT( psValue->pCode != NULL,
		"scriptCodeObjAssignment: Invalid value code pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjAssignment: Invalid generated code block pointer" );

	// Check this is an array
	if (psVariable->psArrayVar->dimensions == 0)
	{
		scr_error("Not an array variable");
		return CE_PARSE;
	}

	ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + 1);	//size + size + opcode

	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the value */
	PUT_BLOCK(ip, psValue);
	FREE_BLOCK(psValue);

	/* Copy in the code for the array index */
	PUT_BLOCK(ip, psVariable);

	/* Code to get the value from the stack into the variable */
	PUT_PKOPCODE(ip, OP_POPARRAYGLOBAL,
		((psVariable->psArrayVar->dimensions << ARRAY_DIMENSION_SHIFT) & ARRAY_DIMENSION_MASK) |
		(psVariable->psArrayVar->index & ARRAY_BASE_MASK) );

	/* Free the variable block */
	FREE_ARRAYBLOCK(psVariable);

	return CE_OK;
}


/* Generate code for getting a value from an array variable */
static CODE_ERROR scriptCodeArrayGet(ARRAY_BLOCK	*psVariable,// The variable to get from
						  CODE_BLOCK	**ppsBlock)	// Generated code
{
	ASSERT( psVariable != NULL,
		"scriptCodeObjAssignment: Invalid object variable block pointer" );
	ASSERT( psVariable->pCode != NULL,
		"scriptCodeObjAssignment: Invalid object variable code pointer" );
	ASSERT( psVariable->psArrayVar != NULL,
		"scriptCodeObjAssignment: Invalid object variable symbol pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjAssignment: Invalid generated code block pointer" );

	// Check this is an array
	if (psVariable->psArrayVar->dimensions == 0)
	{
		scr_error("Not an array variable");
		return CE_PARSE;
	}

	ALLOC_BLOCK(*ppsBlock, psVariable->size + 1);	//size + opcode

	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the array index */
	PUT_BLOCK(ip, psVariable);
	(*ppsBlock)->type = psVariable->psArrayVar->type;

	/* Code to get the value from the array onto the stack */
	PUT_PKOPCODE(ip, OP_PUSHARRAYGLOBAL,
		((psVariable->psArrayVar->dimensions << ARRAY_DIMENSION_SHIFT) & ARRAY_DIMENSION_MASK) |
		(psVariable->psArrayVar->index & ARRAY_BASE_MASK) );

	/* Free the variable block */
	FREE_ARRAYBLOCK(psVariable);

	return CE_OK;
}


/* Generate the final code block for conditional statements */
static CODE_ERROR scriptCodeConditional(
					COND_BLOCK *psCondBlock,	// The intermediate conditional code
					CODE_BLOCK **ppsBlock)		// The final conditional code
{
	UDWORD		i;

	ASSERT( psCondBlock != NULL,
		"scriptCodeConditional: Invalid conditional code block pointer" );
	ASSERT( psCondBlock->pCode != NULL,
		"scriptCodeConditional: Invalid conditional code pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeConditional: Invalid generated code block pointer" );

	/* Allocate the final block */
	ALLOC_BLOCK(*ppsBlock, psCondBlock->size);
	ALLOC_DEBUG(*ppsBlock, psCondBlock->debugEntries);
	ip = (*ppsBlock)->pCode;

	/* Copy the code over */
	PUT_BLOCK(ip, psCondBlock);

	/* Copy the debugging information */
	PUT_DEBUG(*ppsBlock, psCondBlock);

	/* Now set the offsets of jumps in the conditional to the correct value */
	for(i = 0; i < psCondBlock->numOffsets; i++)
	{
		ip = (*ppsBlock)->pCode + psCondBlock->aOffsets[i];
		// *ip = ((*ppsBlock)->size / sizeof(UDWORD)) - (ip - (*ppsBlock)->pCode);

		ip->type = VAL_PKOPCODE;
		ip->v.ival = (*ppsBlock)->size - (ip - (*ppsBlock)->pCode);
		ip->v.ival = (OP_JUMP << OPCODE_SHIFT) | ( (ip->v.ival) & OPCODE_DATAMASK );
	}

	/* Free the original code */
	FREE_DEBUG(psCondBlock);
	FREE_CONDBLOCK(psCondBlock);

	return CE_OK;
}

/* Generate code for function parameters */
static CODE_ERROR scriptCodeParameter(CODE_BLOCK		*psParam,		// Code for the parameter
							 INTERP_TYPE		type,			// Parameter type
							 PARAM_BLOCK	**ppsBlock)		// Generated code
{
	ASSERT( psParam != NULL,
		"scriptCodeParameter: Invalid parameter code block pointer" );
	ASSERT( psParam->pCode != NULL,
		"scriptCodeParameter: Invalid parameter code pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeParameter: Invalid generated code block pointer" );

	RULE("	scriptCodeParameter(): type: %d", type);

	ALLOC_PBLOCK(*ppsBlock, psParam->size, 1);
	ip = (*ppsBlock)->pCode;

	/* Copy in the code for the parameter */
	PUT_BLOCK(ip, psParam);
	FREE_BLOCK(psParam);

	(*ppsBlock)->aParams[0] = type;

	return CE_OK;
}


/* Generate code for binary operators (e.g. 2 + 2) */
static CODE_ERROR scriptCodeBinaryOperator(CODE_BLOCK	*psFirst,	// Code for first parameter
								  CODE_BLOCK	*psSecond,	// Code for second parameter
								  OPCODE		opcode,		// Operator function
								  CODE_BLOCK	**ppsBlock) // Generated code
{
	ALLOC_BLOCK(*ppsBlock, psFirst->size + psSecond->size + 1);		//size + size + binary opcode
	ip = (*ppsBlock)->pCode;

	/* Copy the already generated bits of code into the code block */
	PUT_BLOCK(ip, psFirst);
	PUT_BLOCK(ip, psSecond);

	/* Now put an add operator into the code */
	PUT_PKOPCODE(ip, OP_BINARYOP, opcode);

	/* Free the two code blocks that have been copied */
	FREE_BLOCK(psFirst);
	FREE_BLOCK(psSecond);

	return CE_OK;
}

/* check if the arguments in the function definition body match the argument types
and names from function declaration (if there was any) */
static bool checkFuncParamType(UDWORD argIndex, UDWORD argType)
{
	VAR_SYMBOL		*psCurr;
	SDWORD			i,j;

	if(psCurEvent == NULL)
	{
		debug(LOG_ERROR, "checkFuncParamType() - psCurEvent == NULL");
		return false;
	}

	if(argIndex < psCurEvent->numParams)
	{
		/* find the argument by the index */
		i=psCurEvent->index;
		j=0;
		for(psCurr = psLocalVarsB[i]; psCurr != NULL; psCurr = psCurr->psNext)
		{
			if((psCurEvent->numParams - j - 1)==argIndex)	/* got to the right argument */
			{
				if(argType != psCurr->type)
				{
					debug(LOG_ERROR, "Argument type with index %d in event '%s' doesn't match function declaration (%d/%d)",argIndex,psCurEvent->pIdent,argType,psCurr->type);
					return false;
				}
				else
				{
					//debug(LOG_SCRIPT, "arg matched ");
					return true;
				}
			}
			j++;
		}
	}
	else
	{
		debug(LOG_ERROR, "checkFuncParamType() - argument %d has wrong argument index, event: '%s'", argIndex, psCurEvent->pIdent);
		return false;
	}

	return false;
}


/* Generate code for accessing an object variable.  The variable symbol is
 * stored with the code for the object value so that this block can be used for
 * both setting and retrieving an object value.
 */
static CODE_ERROR scriptCodeObjectVariable(CODE_BLOCK	*psObjCode,	// Code for the object value
								  VAR_SYMBOL	*psVar,		// The object variable symbol
								  OBJVAR_BLOCK	**ppsBlock) // Generated code
{
	ASSERT( psObjCode != NULL,
		"scriptCodeObjectVariable: Invalid object code block pointer" );
	ASSERT( psObjCode->pCode != NULL,
		"scriptCodeObjectVariable: Invalid object code pointer" );
	ASSERT( psVar != NULL,
		"scriptCodeObjectVariable: Invalid variable symbol pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjectVariable: Invalid generated code block pointer" );

	ALLOC_OBJVARBLOCK(*ppsBlock, psObjCode->size, psVar);
	ip = (*ppsBlock)->pCode;

	/* Copy the already generated bit of code into the code block */
	PUT_BLOCK(ip, psObjCode);
	FREE_BLOCK(psObjCode);

	/* Check the variable is the correct type */
	if (psVar->storage != ST_OBJECT)
	{
		scr_error("Only object variables are valid in this context");
		return CE_PARSE;
	}

	return CE_OK;
}


/* Generate code for accessing an array variable.  The variable symbol is
 * stored with the code for the object value so that this block can be used for
 * both setting and retrieving an array value.
 */
static CODE_ERROR scriptCodeArrayVariable(ARRAY_BLOCK	*psArrayCode,	// Code for the array index
								  VAR_SYMBOL	*psVar,			// The array variable symbol
								  ARRAY_BLOCK	**ppsBlock)		// Generated code
{
	ASSERT( psArrayCode != NULL,
		"scriptCodeObjectVariable: Invalid object code block pointer" );
	ASSERT( psArrayCode->pCode != NULL,
		"scriptCodeObjectVariable: Invalid object code pointer" );
	ASSERT( psVar != NULL,
		"scriptCodeObjectVariable: Invalid variable symbol pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeObjectVariable: Invalid generated code block pointer" );

	// Check the variable is the correct type
	if (psVar->dimensions != psArrayCode->dimensions)
	{
		scr_error("Invalid number of array dimensions for this variable");
		return CE_PARSE;
	}

	psArrayCode->psArrayVar = psVar;
	*ppsBlock = psArrayCode;

	return CE_OK;
}


/* Generate code for a constant */
static CODE_ERROR scriptCodeConstant(CONST_SYMBOL	*psConst,	// The object variable symbol
							CODE_BLOCK		**ppsBlock)	// Generated code
{
	ASSERT( psConst != NULL,
		"scriptCodeConstant: Invalid constant symbol pointer" );
	ASSERT( ppsBlock != NULL,
		"scriptCodeConstant: Invalid generated code block pointer" );

	ALLOC_BLOCK(*ppsBlock, 1 + 1);		//OP_PUSH opcode + variable value

	ip = (*ppsBlock)->pCode;
	(*ppsBlock)->type = psConst->type;

	/* Put the value onto the stack */
	switch (psConst->type)
	{
	case VAL_FLOAT:
		RULE("	scriptCodeConstant: pushing float");
		PUT_PKOPCODE(ip, OP_PUSH, VAL_FLOAT);
		PUT_DATA_FLOAT(ip, psConst->fval);
		break;
	case VAL_BOOL:
		RULE("	scriptCodeConstant: pushing bool");
		PUT_PKOPCODE(ip, OP_PUSH, VAL_BOOL);
		PUT_DATA_BOOL(ip, psConst->bval);
		break;
	case VAL_INT:
		RULE("	scriptCodeConstant: pushing int");
		PUT_PKOPCODE(ip, OP_PUSH, VAL_INT);
		PUT_DATA_INT(ip, psConst->ival);
		break;
	case VAL_STRING:
		RULE("	scriptCodeConstant: pushing string");
		PUT_PKOPCODE(ip, OP_PUSH, VAL_STRING);
		PUT_DATA_STRING(ip, psConst->sval);
		break;
	default:	/* Object/user constants */
		RULE("	scriptCodeConstant: pushing object/ user var (type: %d)", psConst->type);
		PUT_PKOPCODE(ip, OP_PUSH, psConst->type);
		PUT_OBJECT_CONST(ip, psConst->oval, psConst->type);		//TODO: differentiate types
		break;
	}

	return CE_OK;
}


/* Generate code for getting a variables value */
static CODE_ERROR scriptCodeVarGet(VAR_SYMBOL		*psVariable,	// The object variable symbol
							CODE_BLOCK		**ppsBlock)		// Generated code
{
	SDWORD size = 1; //1 - for opcode

	if (psVariable->storage == ST_EXTERN)
	{
		// Check there is a set function
		if (psVariable->get == NULL)
		{
			scr_error("No get function for external variable");
			return CE_PARSE;
		}

		size++;
	}

	ALLOC_BLOCK(*ppsBlock, size);
	ip = (*ppsBlock)->pCode;
	(*ppsBlock)->type = psVariable->type;

	/* Code to get the value onto the stack */
	switch (psVariable->storage)
	{
	case ST_PUBLIC:
	case ST_PRIVATE:
		PUT_PKOPCODE(ip, OP_PUSHGLOBAL, psVariable->index);
		break;
	case ST_LOCAL:
		PUT_PKOPCODE(ip, OP_PUSHLOCAL, psVariable->index);	//opcode + event index
		break;
	case ST_EXTERN:
		PUT_PKOPCODE(ip, OP_VARCALL, psVariable->index);
		PUT_VARFUNC(ip, psVariable->get);
		break;
	case ST_OBJECT:
		scr_error("Cannot use member variables in this context");
		return CE_PARSE;
		break;
	default:
		scr_error("Unknown storage type");
		return CE_PARSE;
		break;
	}

	return CE_OK;
}

/* Code Increment/Decrement operators */
static CODE_ERROR scriptCodeIncDec(VAR_SYMBOL		*psVariable,	// The object variable symbol
							CODE_BLOCK		**ppsBlock, OPCODE op_dec_inc)
{

	ALLOC_BLOCK(*ppsBlock, 1 + 1 + 1);	//OP_PUSHREF opcode + variable index + inc opcode
	ip = (*ppsBlock)->pCode;
	(*ppsBlock)->type = psVariable->type;

	/* Put variable index */
	switch (psVariable->storage)
	{
	case ST_PRIVATE:
		PUT_PKOPCODE(ip, OP_PUSHREF, psVariable->type | VAL_REF);
		PUT_DATA_INT(ip, psVariable->index);
		break;
	case ST_LOCAL:
		PUT_PKOPCODE(ip, OP_PUSHLOCALREF, psVariable->type | VAL_REF);
		PUT_DATA_INT(ip, psVariable->index);
		break;
	default:
		scr_error("Wrong variable storage type for increment/decrement operator");
		break;
	}

	/* Put inc/dec opcode */
	PUT_PKOPCODE(ip, OP_UNARYOP, op_dec_inc);

	return CE_OK;
}


/* Generate code for getting a variables value */
static CODE_ERROR scriptCodeVarRef(VAR_SYMBOL		*psVariable,	// The object variable symbol
							PARAM_BLOCK		**ppsBlock)		// Generated code
{
	SDWORD	size;

	//size = sizeof(OPCODE) + sizeof(SDWORD);
	size = 1 + 1;	//OP_PUSHREF opcode + variable index

	ALLOC_PBLOCK(*ppsBlock, size, 1);
	ip = (*ppsBlock)->pCode;

	(*ppsBlock)->aParams[0] = (INTERP_TYPE)(psVariable->type | VAL_REF);

	/* Code to get the value onto the stack */
	switch (psVariable->storage)
	{
	case ST_PUBLIC:
	case ST_PRIVATE:

		PUT_PKOPCODE(ip, OP_PUSHREF, (*ppsBlock)->aParams[0]);
		PUT_DATA_INT(ip, psVariable->index);	//TODO: add new macro
		break;

	case ST_LOCAL:
		PUT_PKOPCODE(ip, OP_PUSHLOCALREF, (*ppsBlock)->aParams[0]);
		PUT_DATA_INT(ip, psVariable->index);
		break;
	case ST_EXTERN:
		scr_error("Cannot use external variables in this context");
		return CE_PARSE;
		break;
	case ST_OBJECT:
		scr_error("Cannot use member variables in this context");
		return CE_PARSE;
		break;
	default:
		scr_error("Unknown storage type: %d", psVariable->storage);
		return CE_PARSE;
		break;
	}

	return CE_OK;
}

/* Change the error action for the ALLOC macro's to what it
 * should be inside a rule body.
 *
 * NOTE: DO NOT USE THE ALLOC MACRO'S INSIDE ANY FUNCTIONS
 *       ONCE ALLOC_ERROR_ACTION IS SET TO THIS VALUE.
 *       ALL FUNCTIONS THAT USE THESE MACROS MUST BE PLACED
 *       BEFORE THIS #define.
 */
#undef ALLOC_ERROR_ACTION
#define ALLOC_ERROR_ACTION YYABORT

%}

%name-prefix="scr_"

%union {
	/* Types returned by the lexer */
	int32_t			bval;
	float			fval;
	SDWORD			ival;
	char			*sval;
	INTERP_TYPE		tval;
	STORAGE_TYPE	stype;
	VAR_SYMBOL		*vSymbol;
	CONST_SYMBOL	*cSymbol;
	FUNC_SYMBOL		*fSymbol;
	TRIGGER_SYMBOL	*tSymbol;
	EVENT_SYMBOL	*eSymbol;
	CALLBACK_SYMBOL	*cbSymbol;

	/* Types only returned by rules */
	CODE_BLOCK		*cblock;
	COND_BLOCK		*condBlock;
	OBJVAR_BLOCK	*objVarBlock;
	ARRAY_BLOCK		*arrayBlock;
	PARAM_BLOCK		*pblock;
	PARAM_DECL		*pdecl;
	TRIGGER_DECL	*tdecl;
	UDWORD			integer_val;
	VAR_DECL		*vdecl;
	VAR_IDENT_DECL	*videcl;
}
	/* key words */
%token lexFUNCTION
%token TRIGGER
%token EVENT
%token WAIT
%token EVERY
%token INACTIVE
%token INITIALISE
%token LINK
%token REF
%token RET
%token _VOID

	/* %token COND */
%token WHILE
%token IF
%token ELSE
%token EXIT
%token PAUSE

	/* boolean operators */
%token BOOLEQUAL
%token NOTEQUAL
%token GREATEQUAL
%token LESSEQUAL
%token GREATER
%token LESS
%token _AND
%token _OR
%token _NOT
%token _INC
%token _DEC

%left _AND _OR
%left BOOLEQUAL NOTEQUAL GREATEQUAL LESSEQUAL GREATER LESS
%nonassoc _NOT
%left '-' '+' '&'
%left '*' '/'
%right TO_INT_CAST
%right TO_FLOAT_CAST
%nonassoc UMINUS
%left _INC _DEC

	/* value tokens */
%token <bval> BOOLEAN_T
%token <fval> FLOAT_T
%token <ival> INTEGER
%token <sval> QTEXT			/* Text with double quotes surrounding it */
%token <tval> TYPE			/* A type specifier */
%token <stype> STORAGE		/* Variable storage type */

%token <sval> IDENT			/* An undefined identifier */

%token <vSymbol> VAR		/* A defined variable */
%token <vSymbol> BOOL_VAR	/* A defined boolean variable */
%token <vSymbol> NUM_VAR	/* A defined numeric (int) variable */
%token <vSymbol> FLOAT_VAR	/* A defined numeric (float) variable */
%token <vSymbol> OBJ_VAR	/* A defined object pointer variable */
%token <vSymbol> STRING_VAR	/* A defined string variable */

%token <vSymbol> VAR_ARRAY		/* A defined variable array */
%token <vSymbol> BOOL_ARRAY		/* A defined boolean variable array */
%token <vSymbol> NUM_ARRAY		/* A defined numeric (int) variable array */
%token <vSymbol> FLOAT_ARRAY	/* A defined numeric (float) variable array */
%token <vSymbol> OBJ_ARRAY		/* A defined object pointer variable array */

%token <vSymbol> BOOL_OBJVAR	/* A defined boolean object member variable */
%token <vSymbol> NUM_OBJVAR		/* A defined numeric object member variable */
%token <vSymbol> USER_OBJVAR	/* A defined user type object member variable */
%token <vSymbol> OBJ_OBJVAR		/* A defined object pointer object member variable */

%token <cSymbol> BOOL_CONSTANT	/* A defined boolean constant */
%token <cSymbol> NUM_CONSTANT	/* A defined numeric constant */
%token <cSymbol> USER_CONSTANT	/* A defined object pointer constant */
%token <cSymbol> OBJ_CONSTANT	/* A defined object pointer constant */
%token <cSymbol> STRING_CONSTANT	/* A defined string constant */

%token <fSymbol> FUNC		/* A defined function */
%token <fSymbol> BOOL_FUNC	/* A defined boolean function */
%token <fSymbol> NUM_FUNC	/* A defined numeric (int) function */
%token <fSymbol> FLOAT_FUNC	/* A defined numeric (float) function */
%token <fSymbol> USER_FUNC	/* A defined user defined type function */
%token <fSymbol> OBJ_FUNC	/* A defined object pointer function */
%token <fSymbol> STRING_FUNC	/* A defined string function */

/* custom, in-script defined functions (events actually) */
%token <eSymbol> VOID_FUNC_CUST		/* A defined function */
%token <eSymbol> BOOL_FUNC_CUST	/* A defined boolean function */
%token <eSymbol> NUM_FUNC_CUST	/* A defined numeric (int) function */
%token <eSymbol> FLOAT_FUNC_CUST	/* A defined numeric (float) function */
%token <eSymbol> USER_FUNC_CUST	/* A defined user defined type function */
%token <eSymbol> OBJ_FUNC_CUST	/* A defined object pointer function */
%token <eSymbol> STRING_FUNC_CUST	/* A defined string function */


%token <tSymbol> TRIG_SYM	/* A defined trigger */
%token <eSymbol> EVENT_SYM	/* A defined event */
%token <cbSymbol> CALLBACK_SYM	/* A callback trigger */

	/* rule types */
%type <cblock> expression
%type <cblock> boolexp
%type <cblock> stringexp
%type <cblock> floatexp
%type <cblock> return_exp
%type <cblock> return_statement
%type <cblock> objexp
%type <cblock> objexp_dot
%type <cblock> userexp
%type <cblock> inc_dec_exp
%type <objVarBlock> num_objvar
%type <objVarBlock> bool_objvar
%type <objVarBlock> user_objvar
%type <objVarBlock> obj_objvar

%type <arrayBlock> array_index
%type <arrayBlock> array_index_list
%type <arrayBlock> num_array_var
%type <arrayBlock> bool_array_var
%type <arrayBlock> user_array_var
%type <arrayBlock> obj_array_var

%type <cblock> statement_list
%type <cblock> statement

%type <cblock> assignment

%type <cblock> func_call
%type <pblock> param_list
/* %type <pblock> braced_param_list */	/* for jump(), to make optional braced parameter list for function calls */
%type <pblock> parameter
%type <pblock> var_ref

%type <cblock> conditional
%type <condBlock> cond_clause
%type <condBlock> terminal_cond
%type <condBlock> cond_clause_list

%type <cblock> loop

%type <videcl>    array_sub_decl
%type <videcl>    array_sub_decl_list
%type <videcl>    variable_ident
%type <vdecl>     variable_decl_head
%type <vdecl>     variable_decl
%type <vdecl>     var_line
%type <tdecl>     trigger_subdecl
%type <integer_val> funcbody_var_def_body
%type <eSymbol> funcbody_var_def
%type <eSymbol> void_funcbody_var_def

/*%type <integer_val> func_decl_ret_type */	/* for function declarations and definitions */
%type <integer_val> funcvar_decl_types
%type <eSymbol>   event_subdecl
%type <eSymbol>   func_subdecl	/* function declaration */
%type <eSymbol>   function_def
%type <eSymbol>   void_function_def
%type <eSymbol>   void_func_subdecl	/* void function declaration */
%type <eSymbol>  function_declaration
%type <eSymbol>  void_function_declaration
%type <eSymbol> function_type

%%

/* 
  The final accepting rule, must always be the first 
  rule, if not explicitly defined as accepting rule
*/
accept_script:			script
				{
					unsigned int i, numArrays;
					SDWORD			size = 0, debug_i = 0, totalArraySize = 0;
					UDWORD			base;
					VAR_SYMBOL		*psCurr;
					TRIGGER_SYMBOL	*psTrig;
					EVENT_SYMBOL	*psEvent;
					UDWORD			numVars;

					RULE("script: var_list");

					// Calculate the code size
					for(psTrig = psTriggers; psTrig; psTrig = psTrig->psNext)
					{
						// Add the trigger code size
						size += psTrig->size;
						debug_i += psTrig->debugEntries;
					}

					for(psEvent = psEvents; psEvent; psEvent = psEvent->psNext)
					{
						// Add the trigger code size
						size += psEvent->size;
						debug_i += psEvent->debugEntries;
					}

					// Allocate the program
					numVars = psGlobalVars ? psGlobalVars->index+1 : 0;
					numArrays = psGlobalArrays ? psGlobalArrays->index+1 : 0;
					for(psCurr=psGlobalArrays; psCurr; psCurr=psCurr->psNext)
					{
						unsigned int arraySize = 1, dimension;
						for(dimension = 0; dimension < psCurr->dimensions; dimension++)
						{
							arraySize *= psCurr->elements[dimension];
						}
						totalArraySize += arraySize;
					}
					ALLOC_PROG(psFinalProg, size, psCurrBlock->pCode,
						numVars, numArrays, numTriggers, numEvents);

					//store local vars
					//allocate array for holding an array of local vars for each event
					psFinalProg->ppsLocalVars = (INTERP_TYPE **)malloc(sizeof(INTERP_TYPE*) * numEvents);
					psFinalProg->ppsLocalVarVal = NULL;
					psFinalProg->numLocalVars = (UDWORD *)malloc(sizeof(UDWORD) * numEvents);	//how many local vars each event has
					psFinalProg->numParams = (UDWORD *)malloc(sizeof(UDWORD) * numEvents);	//how many arguments each event has

					for(psEvent = psEvents, i = 0; psEvent; psEvent = psEvent->psNext, i++)
					{
						psEvent->numLocalVars = numEventLocalVars[i];

						psFinalProg->numLocalVars[i] = numEventLocalVars[i];	//remember how many local vars this event has
						psFinalProg->numParams[i] = psEvent->numParams;	//remember how many parameters this event has

						if(numEventLocalVars[i] > 0)
						{
							unsigned int j;
							INTERP_TYPE * pCurEvLocalVars = (INTERP_TYPE*)malloc(sizeof(INTERP_TYPE) * numEventLocalVars[i]);

							for(psCurr = psLocalVarsB[i], j = 0; psCurr != NULL; psCurr = psCurr->psNext, j++)
							{
								pCurEvLocalVars[numEventLocalVars[i] - j - 1] = psCurr->type;	//save type, order is reversed
							}

							psFinalProg->ppsLocalVars[i] = pCurEvLocalVars;
						}
						else
						{
							psFinalProg->ppsLocalVars[i] = NULL; //this event has no local vars
						}
					}

					ALLOC_DEBUG(psFinalProg, debug_i);
					psFinalProg->debugEntries = 0;
					ip = psFinalProg->pCode;

					// Add the trigger code
					for(psTrig = psTriggers, i = 0; psTrig; psTrig = psTrig->psNext, i++)
					{
						// Store the trigger offset
						psFinalProg->pTriggerTab[i] = (UWORD)(ip - psFinalProg->pCode);
						if (psTrig->pCode != NULL)
						{
							// Store the label
							DEBUG_LABEL(psFinalProg, psFinalProg->debugEntries,
								psTrig->pIdent);
							// Store debug info
							APPEND_DEBUG(psFinalProg, ip - psFinalProg->pCode, psTrig);
							// Store the code
							PUT_BLOCK(ip, psTrig);
							psFinalProg->psTriggerData[i].code = true;
						}
						else
						{
							psFinalProg->psTriggerData[i].code = false;
						}
						// Store the data
						psFinalProg->psTriggerData[i].type = psTrig->type;
						psFinalProg->psTriggerData[i].time = psTrig->time;
					}
					// Note the end of the final trigger
					psFinalProg->pTriggerTab[i] = (UWORD)(ip - psFinalProg->pCode);

					// Add the event code
					for(psEvent = psEvents, i = 0; psEvent; psEvent = psEvent->psNext, i++)
					{
						// Check the event was declared and has a code body
						if (psEvent->pCode == NULL)
						{
							scr_error("Event %s declared without being defined",
								psEvent->pIdent);
							YYABORT;
						}

						// Store the event offset
						psFinalProg->pEventTab[i] = (UWORD)(ip - psFinalProg->pCode);
						// Store the trigger link
						psFinalProg->pEventLinks[i] = (SWORD)(psEvent->trigger);
						// Store the label
						DEBUG_LABEL(psFinalProg, psFinalProg->debugEntries,
							psEvent->pIdent);
						// Store debug info
						APPEND_DEBUG(psFinalProg, ip - psFinalProg->pCode, psEvent);
						// Store the code
						PUT_BLOCK(ip, psEvent);
					}
					// Note the end of the final event
					psFinalProg->pEventTab[i] = (UWORD)(ip - psFinalProg->pCode);

					// Allocate debug info for the variables if necessary
					if (genDebugInfo)
					{
						if (numVars > 0)
						{
							psFinalProg->psVarDebug = (VAR_DEBUG *)malloc(sizeof(VAR_DEBUG) * numVars);
							if (psFinalProg->psVarDebug == NULL)
							{
								scr_error("Out of memory");
								YYABORT;
							}
						}
						else
						{
							psFinalProg->psVarDebug = NULL;
						}
						if (numArrays > 0)
						{
							psFinalProg->psArrayDebug = (ARRAY_DEBUG *)malloc(sizeof(ARRAY_DEBUG) * numArrays);
							if (psFinalProg->psArrayDebug == NULL)
							{
								scr_error("Out of memory");
								YYABORT;
							}
						}
						else
						{
							psFinalProg->psArrayDebug = NULL;
						}
					}
					else
					{
						psFinalProg->psVarDebug = NULL;
						psFinalProg->psArrayDebug = NULL;
					}

					/* Now set the types for the global variables */
					for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psCurr->psNext)
					{
						unsigned int i = psCurr->index;
						psFinalProg->pGlobals[i] = psCurr->type;

						if (genDebugInfo)
						{
							psFinalProg->psVarDebug[i].pIdent = strdup(psCurr->pIdent);
							if (psFinalProg->psVarDebug[i].pIdent == NULL)
							{
								scr_error("Out of memory");
								YYABORT;
							}
							psFinalProg->psVarDebug[i].storage = psCurr->storage;
						}
					}

					/* Now store the array info */
					psFinalProg->arraySize = totalArraySize;
					for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psCurr->psNext)
					{
						unsigned int i = psCurr->index, dimension;

						psFinalProg->psArrayInfo[i].type = psCurr->type;
						psFinalProg->psArrayInfo[i].dimensions = (UBYTE)psCurr->dimensions;
						for(dimension = 0; dimension < psCurr->dimensions; dimension++)
						{
							psFinalProg->psArrayInfo[i].elements[dimension] = (UBYTE)psCurr->elements[dimension];
						}

						if (genDebugInfo)
						{
							psFinalProg->psArrayDebug[i].pIdent = strdup(psCurr->pIdent);
							if (psFinalProg->psArrayDebug[i].pIdent == NULL)
							{
								scr_error("Out of memory");
								YYABORT;
							}
							psFinalProg->psArrayDebug[i].storage = psCurr->storage;
						}
					}
					// calculate the base index of each array
					base = psFinalProg->numGlobals;
					for(i=0; i<numArrays; i++)
					{
						unsigned int arraySize = 1, dimension;

						psFinalProg->psArrayInfo[i].base = base;
						for(dimension = 0; dimension < psFinalProg->psArrayInfo[i].dimensions; dimension++)
						{
							arraySize *= psFinalProg->psArrayInfo[i].elements[dimension];
						}

						base += arraySize;
					}

					RULE("END script: var_list");
				}
			;

script:					var_list trigger_list
						{
							RULE("script:var_list trigger_list");
						}
					|	script event_list
						{
							RULE("script:script event_list");
						}
					|	script var_list
						{
							RULE("script:script var_list");
						}
					|	script trigger_list
						{
							RULE("script:script trigger_list");
						}
					;

var_list:		/* NULL token */
				{
					RULE("var_list: NULL");
				}
			|	var_line
				{
					RULE("var_list: var_line");
					FREE_VARDECL($1);
				}
			|	var_list var_line
				{
					FREE_VARDECL($2);
				}
			;

	/**************************************************************************************
	 *
	 * Variable and function declarations
	 */

var_line:		variable_decl ';'
		{
			/* remember that local var declaration is over */
			localVariableDef = false;
			//debug(LOG_SCRIPT, "localVariableDef = false 0");
			$$ = $1;
		}
		;


variable_decl_head:		STORAGE TYPE
						{
							//debug(LOG_SCRIPT, "variable_decl_head:		STORAGE TYPE");

							ALLOC_VARDECL(psCurrVDecl);
							psCurrVDecl->storage = $1;
							psCurrVDecl->type = $2;

							/* allow local vars to have the same names as global vars (don't check global vars) */
							if($1 == ST_LOCAL)
							{
								localVariableDef = true;
								//debug(LOG_SCRIPT, "localVariableDef = true 0");
							}

							$$ = psCurrVDecl;
							//debug(LOG_SCRIPT, "END variable_decl_head:		STORAGE TYPE (TYPE=%d)", $2);
						}
					|	STORAGE	TRIGGER
						{

							ALLOC_VARDECL(psCurrVDecl);
							psCurrVDecl->storage = $1;
							psCurrVDecl->type = VAL_TRIGGER;

							$$ = psCurrVDecl;
						}
					|	STORAGE	EVENT
						{
							ALLOC_VARDECL(psCurrVDecl);
							psCurrVDecl->storage = $1;
							psCurrVDecl->type = VAL_EVENT;

							$$ = psCurrVDecl;
						}
					;

array_sub_decl:		'[' INTEGER ']'
					{
						if ($2 <= 0 || $2 >= VAR_MAX_ELEMENTS)
						{
							scr_error("Invalid array size %d", $2);
							YYABORT;
						}

						ALLOC_VARIDENTDECL(psCurrVIdentDecl, NULL, 1);
						psCurrVIdentDecl->elements[0] = $2;

						$$ = psCurrVIdentDecl;
					}
			;

array_sub_decl_list: array_sub_decl
					{
						$$ = $1;
					}
			|
					array_sub_decl_list '[' INTEGER ']'
					{
						if ($1->dimensions >= VAR_MAX_DIMENSIONS)
						{
							scr_error("Too many dimensions for array");
							YYABORT;
						}
						if ($3 <= 0 || $3 >= VAR_MAX_ELEMENTS)
						{
							scr_error("Invalid array size %d", $3);
							YYABORT;
						}

						$1->elements[$1->dimensions] = $3;
						$1->dimensions += 1;

						$$ = $1;
					}
			;

variable_ident:		IDENT
					{
						ALLOC_VARIDENTDECL(psCurrVIdentDecl, $1, 0);

						$$ = psCurrVIdentDecl;
					}
			|
					IDENT array_sub_decl_list
					{
						$2->pIdent = strdup($1);
						if ($2->pIdent == NULL)
						{
							scr_error("Out of memory");
							YYABORT;
						}

						$$ = $2;
					}
			;

variable_decl:	variable_decl_head variable_ident
					{
						if (!scriptAddVariable($1, $2))
						{
							/* Out of memory - error already given */
							YYABORT;
						}

						freeVARIDENTDECL($2);

						/* return the variable type */
						$$ = $1;
					}
			|	variable_decl ',' variable_ident
					{
						if (!scriptAddVariable($1, $3))
						{
							/* Out of memory - error already given */
							YYABORT;
						}

						freeVARIDENTDECL($3);

						/* return the variable type */
						$$ = $1;
					}
			;

	/**************************************************************************************
	 *
	 * Trigger declarations
	 */

trigger_list:		/* NULL token */
				|	trigger_decl
				|	trigger_list trigger_decl
				;

trigger_subdecl:	boolexp ',' INTEGER
					{
						ALLOC_TSUBDECL(psCurrTDecl, TR_CODE, $1->size, $3);
						ip = psCurrTDecl->pCode;
						PUT_BLOCK(ip, $1);
						FREE_BLOCK($1);

						$$ = psCurrTDecl;
					}
				|	WAIT ',' INTEGER
					{
						ALLOC_TSUBDECL(psCurrTDecl, TR_WAIT, 0, $3);

						$$ = psCurrTDecl;
					}
				|	EVERY ',' INTEGER
					{
						ALLOC_TSUBDECL(psCurrTDecl, TR_EVERY, 0, $3);

						$$ = psCurrTDecl;
					}
				|	INITIALISE
					{
						ALLOC_TSUBDECL(psCurrTDecl, TR_INIT, 0, 0);

						$$ = psCurrTDecl;
					}
				|	CALLBACK_SYM
					{
						if ($1->numParams != 0)
						{
							scr_error("Expected parameters for callback trigger");
							YYABORT;
						}

						ALLOC_TSUBDECL(psCurrTDecl, $1->type, 0, 0);

						$$ = psCurrTDecl;
					}
				|	CALLBACK_SYM ',' param_list
					{
						RULE("trigger_subdecl: CALLBACK_SYM ',' param_list");
						codeRet = scriptCodeCallbackParams($1, $3, &psCurrTDecl);
						CHECK_CODE_ERROR(codeRet);

						$$ = psCurrTDecl;
					}
				;

trigger_decl:		TRIGGER IDENT '(' trigger_subdecl ')' ';'
					{
						SDWORD	line;
						char	*pDummy;

						scriptGetErrorData(&line, &pDummy);
						if (!scriptAddTrigger($2, $4, (UDWORD)line))
						{
							YYABORT;
						}
						FREE_TSUBDECL($4);
					}
				;

	/**************************************************************************************
	 *
	 * Event declarations
	 */

event_list:			event_decl
				|	event_list event_decl
				;

event_subdecl:		EVENT IDENT
					{
						EVENT_SYMBOL	*psEvent;

						RULE("event_subdecl:		EVENT IDENT");

						if (!scriptDeclareEvent($2, &psEvent,0))
						{
							YYABORT;
						}

						psCurEvent = psEvent;

						$$ = psEvent;

						//debug(LOG_SCRIPT, "END event_subdecl:		EVENT IDENT");
					}
				|	EVENT EVENT_SYM
					{

						RULE("EVENT EVENT_SYM");

						psCurEvent = $2;
						$$ = $2;

						//debug(LOG_SCRIPT, "END EVENT EVENT_SYM");
					}
				;


function_def:		lexFUNCTION TYPE function_type
				{

					RULE("function_def: lexFUNCTION TYPE function_type");

					psCurEvent = $3;

					/* check if this event was declared as function before */
					if(!$3->bFunction)
					{
						debug(LOG_ERROR, "'%s' was declared as event before and can't be redefined to function", $3->pIdent);
						scr_error("Wrong event definition");
						YYABORT;
					}

					$$ = $3;
				}
			;

function_type:			VOID_FUNC_CUST
					|	BOOL_FUNC_CUST
					|	NUM_FUNC_CUST
					|	USER_FUNC_CUST
					|	OBJ_FUNC_CUST
					|	STRING_FUNC_CUST
					|	FLOAT_FUNC_CUST
					;

/* function declaration rules */
func_subdecl:		lexFUNCTION TYPE IDENT
					{
						EVENT_SYMBOL	*psEvent;

						RULE("func_subdecl: lexFUNCTION TYPE IDENT");

						/* allow local vars to have the same names as global vars (don't check global vars) */
						localVariableDef = true;
						//debug(LOG_SCRIPT, "localVariableDef = true 1");

						if (!scriptDeclareEvent($3, &psEvent,0))
						{
							YYABORT;
						}

						psEvent->retType = $2;
						psCurEvent = psEvent;
						psCurEvent->bFunction = true;

						$$ = psEvent;

						//debug(LOG_SCRIPT, "END func_subdecl:lexFUNCTION TYPE IDENT. ");
					}
				;


/* void function declaration rules */
void_func_subdecl:		lexFUNCTION _VOID IDENT	/* declaration of a function */
						{
							EVENT_SYMBOL	*psEvent;

							RULE("void_func_subdecl: lexFUNCTION _VOID IDENT");

							/* allow local vars to have the same names as global vars (don't check global vars) */
							localVariableDef = true;
							//debug(LOG_SCRIPT, "localVariableDef = true 1");

							if (!scriptDeclareEvent($3, &psEvent,0))
							{
								YYABORT;
							}

							psEvent->retType = VAL_VOID;
							psCurEvent = psEvent;
							psCurEvent->bFunction = true;

							$$ = psEvent;

							//debug(LOG_SCRIPT, "END func_subdecl:lexFUNCTION TYPE IDENT. ");
						}
					;

void_function_def:		lexFUNCTION _VOID function_type	/* definition of a function that was declated before */
						{
							//debug(LOG_SCRIPT, "func_subdecl:lexFUNCTION EVENT_SYM ");
							psCurEvent = $3;


							/* check if this event was declared as function before */
							if(!$3->bFunction)
							{
								debug(LOG_ERROR, "'%s' was declared as event before and can't be redefined to function", $3->pIdent);
								scr_error("Wrong event definition");
								YYABORT;
							}

							/* psCurEvent->bFunction = true; */
							/* psEvent->retType = $2; */
							$$ = $3;
							//debug(LOG_SCRIPT, "func_subdecl:lexFUNCTION EVENT_SYM. ");
						}
					;



funcvar_decl_types:		TYPE NUM_VAR
						{
							$$=$1;
						}
					|	TYPE FLOAT_VAR
						{
							$$=$1;
						}
					|	TYPE BOOL_VAR
						{
							$$=$1;
						}
					|	TYPE STRING_VAR
						{
							$$=$1;
						}
					|	TYPE OBJ_VAR
						{
							$$=$1;
						}
					|	TYPE OBJ_OBJVAR
						{
							$$=$1;
						}
					|	TYPE OBJ_ARRAY
						{
							$$=$1;
						}
					|	TYPE VAR	/* STRUCTURESTAT, RESEARCHSTAT etc */
						{
							$$=$1;
						}
					;


funcbody_var_def_body:		 funcvar_decl_types
				{
					if(!checkFuncParamType(0, $1))
					{
						scr_error("Wrong event argument definition in '%s'", psCurEvent->pIdent);
						YYABORT;
					}

					//debug(LOG_SCRIPT, "funcbody_var_def_body=%d \n", $1);
					$$=1;
				}
			| funcbody_var_def_body ',' funcvar_decl_types
				{
					if(!checkFuncParamType($1, $3))
					{
						scr_error("Wrong event argument definition");
						YYABORT;
					}

					//debug(LOG_SCRIPT, "funcbody_var_def_body2=%d \n", $3);
					$$=$1+1;
				}
			;

	/* function was declared before */
funcbody_var_def:	function_def '(' funcbody_var_def_body ')'
					{

						RULE("funcbody_var_def: '(' funcvar_decl_types ')'");

						/* remember that local var declaration is over */
						localVariableDef = false;

						if(psCurEvent == NULL)
						{
							scr_error("psCurEvent == NULL");
							YYABORT;
						}

						if(!psCurEvent->bDeclared)
						{
							debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent);
							scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent);
							YYABORT;
						}

						/* check if number of parameter in body defenition match number of params in the declaration */
						if($3 != psCurEvent->numParams)
						{
							scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						$$ = $1;
					}
				|	function_def '(' ')'
					{

						RULE( "funcbody_var_def: funcbody_var_def_body '(' ')'");

						/* remember that local var declaration is over */
						localVariableDef = false;

						if(psCurEvent == NULL)
						{
							scr_error("psCurEvent == NULL");
							YYABORT;
						}

						if(!psCurEvent->bDeclared)
						{
							debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent);
							scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent);
							YYABORT;
						}

						/* check if number of parameter in body defenition match number of params in the declaration */
						if(0 != psCurEvent->numParams)
						{
							scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						$$ = $1;
					}

				;


	/* void function was declared before */
void_funcbody_var_def:	void_function_def '(' funcbody_var_def_body ')'
					{

						RULE( "funcbody_var_def: '(' funcvar_decl_types ')'");

						/* remember that local var declaration is over */
						localVariableDef = false;

						if(psCurEvent == NULL)
						{
							scr_error("psCurEvent == NULL");
							YYABORT;
						}

						if(!psCurEvent->bDeclared)
						{
							debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent);
							scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent);
							YYABORT;
						}

						/* check if number of parameter in body defenition match number of params in the declaration */
						if($3 != psCurEvent->numParams)
						{
							scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						$$ = $1;
					}
				|	void_function_def '(' ')'
					{

						RULE( "funcbody_var_def: funcbody_var_def_body '(' ')'");

						/* remember that local var declaration is over */
						localVariableDef = false;

						if(psCurEvent == NULL)
						{
							scr_error("psCurEvent == NULL");
							YYABORT;
						}

						if(!psCurEvent->bDeclared)
						{
							debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent);
							scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent);
							YYABORT;
						}

						/* check if number of parameter in body defenition match number of params in the declaration */
						if(0 != psCurEvent->numParams)
						{
							scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						$$ = $1;
					}

				;


/* event declaration with parameters, like: event myEvent(int Arg1, string Arg2); */
argument_decl_head:		TYPE variable_ident
				{

					RULE( "argument_decl_head: TYPE variable_ident");


					/* handle type part */
					ALLOC_VARDECL(psCurrVDecl);
					psCurrVDecl->storage =ST_LOCAL;	/* can only be local */
					psCurrVDecl->type = $1;

					/* handle ident part */
					if (!scriptAddVariable(psCurrVDecl, $2))
					{
						YYABORT;
					}

					freeVARIDENTDECL($2);

					FREE_VARDECL(psCurrVDecl);

					/* return the variable type */
					/* $$ = psCurrVDecl; */ /* not needed? */

					if(psCurEvent == NULL)
						debug(LOG_ERROR, "argument_decl_head 0:  - psCurEvent == NULL");

					psCurEvent->numParams = psCurEvent->numParams + 1;	/* remember a parameter was declared */

					/* remember parameter type */
					psCurEvent->aParams[0] = $1;

					//debug(LOG_SCRIPT, "argument_decl_head 0. ");
				}
			|	argument_decl_head ',' TYPE variable_ident
				{
					//debug(LOG_SCRIPT, "argument_decl_head 1 ");

					/* handle type part */
					ALLOC_VARDECL(psCurrVDecl);
					psCurrVDecl->storage =ST_LOCAL;	/* can only be local */
					psCurrVDecl->type = $3;

					/* remember parameter type */
					psCurEvent->aParams[psCurEvent->numParams] = $3;
					//debug(LOG_SCRIPT, "argument_decl_head 10 ");

					/* handle ident part */
					if (!scriptAddVariable(psCurrVDecl, $4))
					{
						YYABORT;
					}
					//debug(LOG_SCRIPT, "argument_decl_head 11 ");
					freeVARIDENTDECL($4);
					FREE_VARDECL(psCurrVDecl);

					/* return the variable type */
					/* $$ = psCurrVDecl; */ /* not needed? */

					if(psCurEvent == NULL)
						debug(LOG_ERROR, "argument_decl_head 0:  - psCurEvent == NULL");
					psCurEvent->numParams = psCurEvent->numParams + 1;	/* remember a parameter was declared */

					//debug(LOG_SCRIPT, "argument_decl_head 1. ");
				}
			;
argument_decl:	'(' ')'
			|	'(' argument_decl_head ')'
				{
					/* remember that local var declaration is over */
					localVariableDef = false;
					//debug(LOG_SCRIPT, "localVariableDef = false 1");
				}
			;

function_declaration:		func_subdecl '(' argument_decl_head ')'	/* function was not declared before */
				{
					RULE( "function_declaration: func_subdecl '(' argument_decl_head ')'");

					/* remember that local var declaration is over */
					localVariableDef = false;
					//debug(LOG_SCRIPT, "localVariableDef = false 2");

					if(psCurEvent->bDeclared) /* can only occur if different (new) var names are used in the event definition that don't match with declaration*/
					{
						scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent);
						YYABORT;
					}

					$$ = $1;
				}
			|	func_subdecl '(' ')'
				{
					RULE( "function_declaration: func_subdecl '(' ')'");

					/* remember that local var declaration is over */
					localVariableDef = false;
					//debug(LOG_SCRIPT, "localVariableDef = false 3");

					if($1->numParams > 0 && psCurEvent->bDeclared) /* can only occur if no parameters or different (new) var names are used in the event definition that don't match with declaration */
					{
						scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent);
						YYABORT;
					}

					$$ = $1;
				}

			;



void_function_declaration:		void_func_subdecl '(' argument_decl_head ')'	/* function was not declared before */
				{
					/* remember that local var declaration is over */
					localVariableDef = false;
					//debug(LOG_SCRIPT, "localVariableDef = false 2");

					if(psCurEvent->bDeclared) /* can only occur if different (new) var names are used in the event definition that don't match with declaration*/
					{
						scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent);
						YYABORT;
					}

					$$ = $1;
				}
			|	void_func_subdecl '(' ')'
				{

						RULE( "void_function_declaration: void_func_subdecl '(' ')'");

					/* remember that local var declaration is over */
					localVariableDef = false;
					//debug(LOG_SCRIPT, "localVariableDef = false 3");

					if($1->numParams > 0 && psCurEvent->bDeclared) /* can only occur if no parameters or different (new) var names are used in the event definition that don't match with declaration */
					{
						scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent);
						YYABORT;
					}

					$$ = $1;
				}

			;


return_statement_void:	RET ';'
			;

return_statement:	return_statement_void
					{

						RULE( "return_statement: return_statement_void");

						if(psCurEvent == NULL)	/* no events declared or defined yet */
						{
							scr_error("return statement outside of function");
							YYABORT;
						}

						if(!psCurEvent->bFunction)
						{
							scr_error("return statement inside of an event '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						if(psCurEvent->retType != VAL_VOID)
						{
							scr_error("wrong return statement syntax for a non-void function '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						/* Allocate code block for exit instruction */
						ALLOC_BLOCK(psCurrBlock, 1);	//1 for opcode
						ip = psCurrBlock->pCode;
						PUT_OPCODE(ip, OP_EXIT);

						psCurrBlock->type = VAL_VOID;	/* make return statement of type VOID manually */

						$$ = psCurrBlock;
					}
					|	RET  return_exp  ';'
					{

						RULE( "return_statement: RET return_exp ';'");

						if(psCurEvent == NULL)	/* no events declared or defined yet */
						{
							debug(LOG_ERROR, "return statement outside of function");
							YYABORT;
						}

						if(!psCurEvent->bFunction)
						{
							debug(LOG_ERROR, "return statement inside of an event '%s'", psCurEvent->pIdent);
							YYABORT;
						}

						if(psCurEvent->retType != $2->type)
						{
							if(!interpCheckEquiv(psCurEvent->retType, $2->type))
							{
								debug(LOG_ERROR, "return type mismatch");
								debug(LOG_ERROR, "wrong return statement syntax for function '%s' (%d - %d)", psCurEvent->pIdent, psCurEvent->retType, $2->type);
								YYABORT;
							}
						}

						/* Allocate code block for exit instruction */
						ALLOC_BLOCK(psCurrBlock, $2->size + 1);	//+1 for OPCODE

						ip = psCurrBlock->pCode;

						PUT_BLOCK(ip, $2);

						PUT_OPCODE(ip, OP_EXIT);

						/* store the type of the exp */
						psCurrBlock->type = $2->type;
						
						FREE_BLOCK($2);

						$$ = psCurrBlock;

						//debug(LOG_SCRIPT, "END RET  return_exp  ';'");
					}
			;


statement_list:		/* NULL token */
						{
							RULE( "statement_list: NULL");

							// Allocate a dummy code block
							ALLOC_BLOCK(psCurrBlock, 1);
							psCurrBlock->size = 0;

							$$ = psCurrBlock;
						}
				|	statement
						{
							RULE("statement_list: statement");
							$$ = $1;
						}
				|	statement_list statement
						{
							RULE("statement_list: statement_list statement");

							ALLOC_BLOCK(psCurrBlock, $1->size + $2->size);

							ALLOC_DEBUG(psCurrBlock, $1->debugEntries +
													 $2->debugEntries);
							ip = psCurrBlock->pCode;

							/* Copy the two code blocks */
							PUT_BLOCK(ip, $1);
							PUT_BLOCK(ip, $2);
							PUT_DEBUG(psCurrBlock, $1);
							APPEND_DEBUG(psCurrBlock, $1->size / sizeof(INTERP_VAL), $2);

							ASSERT($1 != NULL, "$1 == NULL");
							ASSERT($1->psDebug != NULL, "psDebug == NULL");

							FREE_DEBUG($1);
							FREE_DEBUG($2);
							FREE_BLOCK($1);
							FREE_BLOCK($2);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				;


event_decl:			event_subdecl ';'
					{
						RULE( "event_decl: event_subdecl ';'");

						psCurEvent->bDeclared = true;
					}
				|	func_subdecl argument_decl ';'	/* event (function) declaration can now include parameter declaration (optional) */
					{
						//debug(LOG_SCRIPT, "localVariableDef = false new ");
						localVariableDef = false;
						psCurEvent->bDeclared = true;
					}
				|	void_func_subdecl argument_decl ';'	/* event (function) declaration can now include parameter declaration (optional) */
					{
						RULE( "event_decl: void_func_subdecl argument_decl ';'");

						//debug(LOG_SCRIPT, "localVariableDef = false new ");
						localVariableDef = false;
						psCurEvent->bDeclared = true;
					}
				|	event_subdecl '(' TRIG_SYM ')' '{' var_list statement_list '}' 	/* 16.08.05 - local vars support */
					{
						RULE( "event_decl: event_subdecl '(' TRIG_SYM ')'");

						/* make sure this event is not declared as function */
						if(psCurEvent->bFunction)
						{
							debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent);
							scr_error("Wrong event definition");
							YYABORT;
						}

						/* pop required number of paramerets passed to this event (if any) */
						/* popArguments($1, $7, &psCurrBlock); */

						/* if (!scriptDefineEvent($1, $6, $3->index)) */
						if (!scriptDefineEvent($1, $7, $3->index))
						{
							YYABORT;
						}

						/* end of event */
						psCurEvent = NULL;

						FREE_DEBUG($7);
						FREE_BLOCK($7);

						RULE( "END event_decl: event_subdecl '(' TRIG_SYM ')'");

					}
				|	event_subdecl '(' trigger_subdecl ')'
					{
						// Get the line for the implicit trigger declaration
						char	*pDummy;

						RULE( "event_decl:event_subdecl '(' trigger_subdecl ')' ");

						/* make sure this event is not declared as function */
						if(psCurEvent->bFunction)
						{
							debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent);
							scr_error("Wrong event definition");
							YYABORT;
						}

						scriptGetErrorData((SDWORD *)&debugLine, &pDummy);
					}
				'{' var_list statement_list '}'	/* local vars support */
					{
						RULE("event_decl: '{' var_list statement_list '}'");

						// Create a trigger for this event
						if (!scriptAddTrigger("", $3, debugLine))
						{
							YYABORT;
						}
						FREE_TSUBDECL($3);

						/* if (!scriptDefineEvent($1, $7, numTriggers - 1)) */
						if (!scriptDefineEvent($1, $8, numTriggers - 1))
						{
							YYABORT;
						}

						/* end of event */
						psCurEvent = NULL;

						FREE_DEBUG($8);
						FREE_BLOCK($8);

						RULE( "END event_decl:event_subdecl '(' trigger_subdecl ')' .");
					}

				/* local vars */
				|	event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}'
					{
						RULE( "event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}'");

						/* make sure this event is not declared as function */
						if(psCurEvent->bFunction)
						{
							debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent);
							scr_error("Wrong event definition");
							YYABORT;
						}

						if (!scriptDefineEvent($1, $7, -1))
						{
							YYABORT;
						}

						/* end of event */
						psCurEvent = NULL;

						FREE_DEBUG($7);
						FREE_BLOCK($7);

						RULE( "END event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}'");
					}

				/* VOID function that was NOT declared before */
				|	void_function_declaration  '{' var_list statement_list  '}'
					{
						RULE( "void_function_declaration  '{' var_list statement_list  '}'");

						/* stays the same if no params (just gets copied) */
						ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* statement_list + opcode + numParams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy the two code blocks */
						PUT_BLOCK(ip, $4);
						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						ALLOC_DEBUG(psCurrBlock, $4->debugEntries);
						PUT_DEBUG(psCurrBlock, $4);

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_DEBUG($4);
						FREE_BLOCK($4);

						/* end of event */
						psCurEvent = NULL;

						/* free block since code was copied in scriptDefineEvent() */
						FREE_DEBUG(psCurrBlock);
						FREE_BLOCK(psCurrBlock);

						RULE( "END void_function_declaration  '{' var_list statement_list  '}'");
					}

					/* void function that was declared before */
				 |	void_funcbody_var_def '{' var_list statement_list '}'
					{

						RULE( "void_funcbody_var_def '{' var_list statement_list '}'");

						/* stays the same if no params (just gets copied) */
						ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams);	/* statements + opcode + numparams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy the old (main) code and free it */
						PUT_BLOCK(ip, $4);
						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						/* copy debug info */
						ALLOC_DEBUG(psCurrBlock, $4->debugEntries);
						PUT_DEBUG(psCurrBlock, $4);

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_DEBUG($4);
						FREE_BLOCK($4);
						FREE_DEBUG(psCurrBlock);
						FREE_BLOCK(psCurrBlock);

						/* end of event */
						psCurEvent = NULL;

						RULE( "END void_func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list '}'");
					}

				/* function that was NOT declared before (atleast must look like this)!!! */
				|	function_declaration  '{' var_list statement_list return_statement  '}'
					{
						RULE( "function_declaration  '{' var_list statement_list return_statement  '}'");

						/* stays the same if no params (just gets copied) */
						//ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + sizeof(OPCODE) + (sizeof(OPCODE) * $1->numParams)); /* statement_list + expression + EXIT + numParams */
						ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + 1 + $1->numParams); /* statement_list + return_expr + EXIT opcode + numParams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy the two code blocks */
						PUT_BLOCK(ip, $4);

						PUT_BLOCK(ip, $5);

						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						ALLOC_DEBUG(psCurrBlock, $4->debugEntries);

						PUT_DEBUG(psCurrBlock, $4);

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_DEBUG($4);
						FREE_BLOCK($4);
						FREE_BLOCK($5);

						/* end of event */
						psCurEvent = NULL;

						/* free block since code was copied in scriptDefineEvent() */
						FREE_DEBUG(psCurrBlock);
						FREE_BLOCK(psCurrBlock);

						RULE( "END function_declaration  '{' var_list statement_list return_statement  '}'");
					}

				/* function that WAS declared before */
				|	funcbody_var_def '{' var_list statement_list return_statement '}'
					{
						RULE( "func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list return_statement '}'");

						/* stays the same if no params (just gets copied) */
						//ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + sizeof(OPCODE) + (sizeof(OPCODE) * $1->numParams));
						ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + 1 + $1->numParams);	/* statements + return expr + opcode + numParams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy the old (main) code and free it */
						PUT_BLOCK(ip, $4);
						PUT_BLOCK(ip, $5);
						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						/* copy debug info */
						ALLOC_DEBUG(psCurrBlock, $4->debugEntries);
						PUT_DEBUG(psCurrBlock, $4);

						RULE( "debug entries %d", $4->debugEntries);

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_DEBUG($4);
						FREE_BLOCK($4);
						FREE_BLOCK($5);
						FREE_DEBUG(psCurrBlock);
						FREE_BLOCK(psCurrBlock);

						/* end of event */
						psCurEvent = NULL;

						RULE( "END func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list return_statement '}'");
					}

				/* function that WAS declared before with a single return statement  */
				|	funcbody_var_def '{' var_list return_statement '}'
					{
						RULE( "funcbody_var_def '{' var_list return_statement '}'");

						/* stays the same if no params (just gets copied) */
						ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams);	/*  return expr + opcode + numParams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy the old (main) code and free it */
						PUT_BLOCK(ip, $4);
						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_BLOCK($4);
						FREE_BLOCK(psCurrBlock);
						psCurEvent = NULL;

						RULE( "END funcbody_var_def '{' var_list return_statement '}'");
					}
				/* function with a single return statement that was NOT declared before */
				|	function_declaration '{' var_list return_statement  '}'
					{
						RULE( "function_declaration  '{' var_list statement_list return_statement  '}'");

						/* stays the same if no params (just gets copied) */
						ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* return_expr + EXIT opcode + numParams */
						ip = psCurrBlock->pCode;

						/* pop required number of paramerets passed to this event (if any) */
						popArguments(&ip, $1->numParams);

						/* Copy code block */
						PUT_BLOCK(ip, $4);
						PUT_OPCODE(ip, OP_EXIT);		/* must exit after return */

						if (!scriptDefineEvent($1, psCurrBlock, -1))
						{
							YYABORT;
						}

						FREE_BLOCK($4);
						psCurEvent = NULL;
						FREE_BLOCK(psCurrBlock);

						RULE( "END function_declaration  '{' var_list statement_list return_statement  '}'");
					}
				;

	/**************************************************************************************
	 *
	 * Statements
	 */

statement:			assignment ';'
					{
						UDWORD line;
						char *pDummy;

						RULE("statement: assignment");

						/* Put in debugging info */
						if (genDebugInfo)
						{
							ALLOC_DEBUG($1, 1);
							$1->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							$1->psDebug[0].line = line;
						}

						$$ = $1;
					}
				|	inc_dec_exp ';'
					{
						UDWORD line;
						char *pDummy;

						RULE("statement: inc_dec_exp");

						/* Put in debugging info */
						if (genDebugInfo)
						{
							ALLOC_DEBUG($1, 1);
							$1->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							$1->psDebug[0].line = line;
						}

						$$ = $1;
					}
				|	func_call ';'
					{
						UDWORD line;
						char *pDummy;

						RULE("statement: func_call ';'");

						/* Put in debugging info */
						if (genDebugInfo)
						{
							ALLOC_DEBUG($1, 1);
							$1->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							$1->psDebug[0].line = line;
						}

						$$ = $1;
					}
			|	VOID_FUNC_CUST '(' param_list ')'  ';'
					{
						UDWORD line,paramNumber;
						char *pDummy;

						RULE( "statement: VOID_FUNC_CUST '(' param_list ')'  ';'");

						/* allow to call EVENTs to reuse the code only if no actual parameters are specified in function call, like "myEvent();" */
						if(!$1->bFunction && $3->numParams > 0)
						{
							scr_error("Can't pass any parameters in an event call:\nEvent: '%s'", $1->pIdent);
							return CE_PARSE;
						}

						if($3->numParams != $1->numParams)
						{
							scr_error("Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							return CE_PARSE;
						}

						/* check if right parameters were passed */
						paramNumber = checkFuncParamTypes($1, $3);
						if(paramNumber > 0)
						{
							debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//paramList + opcode + event index
						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);			//Put event index

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
					}
			|	EVENT_SYM '(' param_list ')'  ';'		/* event as a void function to reuse the code */
					{
						UDWORD line;
						char *pDummy;

						RULE( "statement: EVENT_SYM '(' param_list ')'  ';'");

						/* allow to call EVENTs to reuse the code only if no actual parameters are specified in function call, like "myEvent();" */
						if(!$1->bFunction && $3->numParams > 0)
						{
							scr_error("Can't pass any parameters in an event call:\nEvent: '%s'", $1->pIdent);
							return CE_PARSE;
						}

						if($3->numParams != $1->numParams)
						{
							scr_error("Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							return CE_PARSE;
						}

						/* Allocate the code block */
						//ALLOC_BLOCK(psCurrBlock, $3->size + sizeof(OPCODE) + sizeof(UDWORD));	//Params + Opcode + event index
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index
						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

							/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);			//Put event index as VAL_EVENT

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
					}
				|	conditional
					{
						$$ = $1;
					}
				|	loop
					{
						$$ = $1;
					}
				|	EXIT ';'
					{
						UDWORD line;
						char *pDummy;

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, 1);	//1 - Exit opcode
						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						/* Store the instruction */
						PUT_OPCODE(ip, OP_EXIT);

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
					}
				|	return_statement
					{
						UDWORD line;
						char *pDummy;

						RULE( "statement: return_statement");

						if(psCurEvent == NULL)
						{
							scr_error("'return' outside of function body");
							YYABORT;
						}

						if(!psCurEvent->bFunction)
						{
							debug(LOG_ERROR, "'return' can only be used in functions, not in events, event: '%s'", psCurEvent->pIdent);
							scr_error("'return' in event");
							YYABORT;
						}

						if($1->type != psCurEvent->retType)
						{
							debug(LOG_ERROR, "'return' type doesn't match with function return type, function: '%s' (%d / %d)", psCurEvent->pIdent, $1->type, psCurEvent->retType);
							scr_error("'return' type mismatch 0");
							YYABORT;
						}

						ALLOC_BLOCK(psCurrBlock, $1->size + 1);	//return statement + EXIT opcode
						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						PUT_BLOCK(ip, $1);
						PUT_OPCODE(ip, OP_EXIT);

						FREE_BLOCK($1);

						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
					}
				|	PAUSE '(' INTEGER ')' ';'
					{
						UDWORD line;
						char *pDummy;

						// can only have a positive pause
						if ($3 < 0)
						{
							scr_error("Invalid pause time");
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, 1);	//1 - for OP_PAUSE
						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						/* Store the instruction */
						PUT_PKOPCODE(ip, OP_PAUSE, $3);

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
					}
				;

/* function return type */
return_exp:		expression
				{
					RULE("return_exp: expression");

					/* Just pass the code up the tree */
					$1->type = VAL_INT;
					$$ = $1;
				}
		|	floatexp
				{
					RULE("return_exp: floatexp");

					/* Just pass the code up the tree */
					$1->type = VAL_FLOAT;
					$$ = $1;
				}
		|	stringexp
				{
					RULE( "return_exp: stringexp");

					/* Just pass the code up the tree */
					$1->type = VAL_STRING;
					$$ = $1;
				}
		|	boolexp
				{
					RULE( "return_exp: boolexp");

					/* Just pass the code up the tree */
					$1->type = VAL_BOOL;
					$$ = $1;
				}
		|	objexp		/* example: "group", "feature", "structure.health",  "droid.numkills" */
				{
					RULE( "return_exp: objexp");

					/* Just pass the code up the tree */
					/* $1->type =  */
					$$ = $1;
				}
		|	userexp		/* templates, bodies etc */
				{
					RULE( "return_exp: userexp");

					/* Just pass the code up the tree */
					/* $1->type =  */
					$$ = $1;
				}
		;


	/* Variable assignment statements, eg:
	 *        foo = 4 + bar ;
	 */
assignment:			NUM_VAR '=' expression
						{
							RULE( "assignment: NUM_VAR '=' expression");

							codeRet = scriptCodeAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	BOOL_VAR '=' boolexp
						{
							RULE( "assignment: BOOL_VAR '=' boolexp");

							codeRet = scriptCodeAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	FLOAT_VAR '=' floatexp
						{
							RULE( "assignment: FLOAT_VAR '=' floatexp");

							codeRet = scriptCodeAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	STRING_VAR '=' stringexp
						{
							RULE("assignment: STRING_VAR '=' stringexp");

							codeRet = scriptCodeAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	OBJ_VAR '=' objexp
						{
							RULE("assignment: OBJ_VAR '=' objexp");

							if (!interpCheckEquiv($1->type, $3->type))
							{
								scr_error("User type mismatch for assignment (%d - %d) 4", $1->type, $3->type);
								YYABORT;
							}
							codeRet = scriptCodeAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	VAR '=' userexp
						{
							RULE( "assignment: VAR '=' userexp");

							if (!interpCheckEquiv($1->type, $3->type))
							{
								scr_error("User type mismatch for assignment (%d - %d) 3", $1->type, $3->type);
								YYABORT;
							}
							codeRet = scriptCodeAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	num_objvar '=' expression
						{
							RULE( "assignment: num_objvar '=' expression");

							codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	bool_objvar '=' boolexp
						{
							RULE( "assignment: bool_objvar '=' boolexp");

							codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	user_objvar '=' userexp
						{
							RULE( "assignment: user_objvar '=' userexp");

							if (!interpCheckEquiv($1->psObjVar->type,$3->type))
							{
								scr_error("User type mismatch for assignment (%d - %d) 2", $1->psObjVar->type, $3->type);
								YYABORT;
							}
							codeRet = scriptCodeObjAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	obj_objvar '=' objexp
						{
							RULE( "assignment: obj_objvar '=' objexp");

							if (!interpCheckEquiv($1->psObjVar->type, $3->type))
							{
								scr_error("User type mismatch for assignment (%d - %d) 1", $1->psObjVar->type, $3->type);
								YYABORT;
							}
							codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	num_array_var '=' expression
						{
							RULE( "assignment: num_array_var '=' expression");

							codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	bool_array_var '=' boolexp
						{
							RULE( "assignment: bool_array_var '=' boolexp");

							codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	user_array_var '=' userexp
						{
							RULE( "assignment: user_array_var '=' userexp");

							if (!interpCheckEquiv($1->psArrayVar->type,$3->type))
							{
								scr_error("User type mismatch for assignment (%d - %d) 0", $1->psArrayVar->type, $3->type);
								YYABORT;
							}
							codeRet = scriptCodeArrayAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				|	obj_array_var '=' objexp
						{
							RULE( "assignment: obj_array_var '=' objexp");

							if (!interpCheckEquiv($1->psArrayVar->type, $3->type))
							{
								scr_error("User type mismatch for assignment");
								YYABORT;
							}
							codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock);
							CHECK_CODE_ERROR(codeRet);

							/* Return the code block */
							$$ = psCurrBlock;
						}
				;

	/* Function calls, eg:
	 *         DisplayGlobals() ;
	 *
	 *		   If the function returns a value in a statement context
	 *		   code is generated to remove that value from the stack.
	 */
func_call:		NUM_FUNC '(' param_list ')'
					{
						RULE( "func_call: NUM_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	BOOL_FUNC '(' param_list ')'
					{
						RULE( "func_call: BOOL_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	USER_FUNC '(' param_list ')'
					{
						RULE( "func_call: USER_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	OBJ_FUNC '(' param_list ')'
					{
						RULE( "func_call: OBJ_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	FUNC '(' param_list ')'
					{
						RULE("func_call: FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);

						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	STRING_FUNC '(' param_list ')'
					{
						RULE( "func_call: STRING_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			|	FLOAT_FUNC '(' param_list ')'
					{
						RULE( "func_call: FLOAT_FUNC '(' param_list ')'");

						/* Generate the code for the function call */
						codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrBlock;
					}
			;


	/*
	 * Parse parameter lists eg:
	 *		5, foo, (bar + 4.2) / 3, foobar and barfoo
	 *
	 * These rules return PARAM_BLOCKS which contain the type of each
	 * parameter as well as the code for the parameter.
	 */

param_list:		/* NULL token */
					{
						/* create a dummy pblock containing nothing */
						//ALLOC_PBLOCK(psCurrPBlock, sizeof(UDWORD), 1);
						ALLOC_PBLOCK(psCurrPBlock, 1, 1);	//1 is enough
						psCurrPBlock->size = 0;
						psCurrPBlock->numParams = 0;

						$$ = psCurrPBlock;
					}
			|	parameter
					{
						RULE("param_list: parameter");
						$$ = $1;
					}
			|	param_list ',' parameter
					{
						RULE("param_list: param_list ',' parameter");

						ALLOC_PBLOCK(psCurrPBlock, $1->size + $3->size, $1->numParams + $3->numParams);
						ip = psCurrPBlock->pCode;

						/* Copy in the code for the parameters */
						PUT_BLOCK(ip, $1);
						PUT_BLOCK(ip, $3);

						/* Copy the parameter types */
						memcpy(psCurrPBlock->aParams, $1->aParams,
								$1->numParams * sizeof(INTERP_TYPE));
						memcpy(psCurrPBlock->aParams + $1->numParams,
								$3->aParams,
								$3->numParams * sizeof(INTERP_TYPE));

						/* Free the old pblocks */
						FREE_PBLOCK($1);
						FREE_PBLOCK($3);

						/* return the pblock */
						$$ = psCurrPBlock;
					}
			;
parameter:		expression
					{
						RULE("parameter: expression");

						/* Generate the code for the parameter                     */
						codeRet = scriptCodeParameter($1, VAL_INT, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	boolexp
					{
						RULE("parameter: boolexp (boolexp size: %d)", $1->size);

						/* Generate the code for the parameter */
						codeRet = scriptCodeParameter($1, VAL_BOOL, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	floatexp
					{
						RULE("parameter: floatexp");

						/* Generate the code for the parameter */
						codeRet = scriptCodeParameter($1, VAL_FLOAT, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	stringexp
					{
						RULE("parameter: stringexp");

						/* Generate the code for the parameter */
						codeRet = scriptCodeParameter($1, VAL_STRING, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	userexp
					{
						RULE("parameter: userexp");

						/* Generate the code for the parameter */
						codeRet = scriptCodeParameter((CODE_BLOCK *)$1, $1->type, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	objexp
					{
						RULE( "parameter: objexp");

						/* Generate the code for the parameter */
						codeRet = scriptCodeParameter($1, $1->type, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	var_ref
					{
						/* just pass the variable reference up the tree */
						$$ = $1;
					}
			;

var_ref:		REF NUM_VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	REF FLOAT_VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	REF BOOL_VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	REF STRING_VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	REF VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			|	REF OBJ_VAR
					{
						codeRet = scriptCodeVarRef($2, &psCurrPBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrPBlock;
					}
			;

	/*
	 * Conditional statements, can be :
	 *
	 */

conditional:		cond_clause_list
					{
						RULE( "conditional: cond_clause_list");

						codeRet = scriptCodeConditional($1, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						$$ = psCurrBlock;
					}
				|	cond_clause_list ELSE terminal_cond
					{
						RULE( "conditional: cond_clause_list ELSE terminal_cond");

						ALLOC_CONDBLOCK(psCondBlock, $1->numOffsets + $3->numOffsets, $1->size + $3->size);
						ALLOC_DEBUG(psCondBlock, $1->debugEntries + $3->debugEntries);
						ip = psCondBlock->pCode;

						/* Store the two blocks of code */
						PUT_BLOCK(ip, $1);
						PUT_BLOCK(ip, $3);

						/* Copy over the offset information       */
						/* (There isn't any in the terminal_cond) */
						memcpy(psCondBlock->aOffsets, $1->aOffsets,
							   $1->numOffsets * sizeof(SDWORD));
						psCondBlock->numOffsets = $1->numOffsets;

						/* Put in the debugging information */
						PUT_DEBUG(psCondBlock, $1);
						APPEND_DEBUG(psCondBlock, $1->size, $3);

						/* Free the code blocks */
						FREE_DEBUG($1);
						FREE_DEBUG($3);
						FREE_CONDBLOCK($1);
						FREE_CONDBLOCK($3);

						/* Do the final processing of the conditional */
						codeRet = scriptCodeConditional(psCondBlock, &psCurrBlock);
						CHECK_CODE_ERROR(codeRet);

						$$ = psCurrBlock;
					}
				;

cond_clause_list:	cond_clause
					{
						RULE( "cond_clause_list:	cond_clause");

						$$ = $1;
					}
				|	cond_clause_list ELSE cond_clause
					{
						RULE( "cond_clause_list:	cond_clause_list ELSE cond_clause");

						ALLOC_CONDBLOCK(psCondBlock,
										$1->numOffsets + $3->numOffsets,
										$1->size + $3->size);
						ALLOC_DEBUG(psCondBlock, $1->debugEntries + $3->debugEntries);
						ip = psCondBlock->pCode;

						/* Store the two blocks of code */
						PUT_BLOCK(ip, $1);
						PUT_BLOCK(ip, $3);

						/* Copy over the offset information */
						memcpy(psCondBlock->aOffsets, $1->aOffsets,
							   $1->numOffsets * sizeof(SDWORD));
						psCondBlock->aOffsets[$1->numOffsets] =
							$3->aOffsets[0] + $1->size;
						psCondBlock->numOffsets = $1->numOffsets + 1;

						/* Put in the debugging information */
						PUT_DEBUG(psCondBlock, $1);
						APPEND_DEBUG(psCondBlock, $1->size, $3);

						/* Free the code blocks */
						FREE_DEBUG($1);
						FREE_DEBUG($3);
						FREE_CONDBLOCK($1);
						FREE_CONDBLOCK($3);

						$$ = psCondBlock;
					}
				;


terminal_cond:		'{' statement_list '}'
					{
						RULE( "terminal_cond: '{' statement_list '}'");

						/* Allocate the block */
						ALLOC_CONDBLOCK(psCondBlock, 1, $2->size);
						ALLOC_DEBUG(psCondBlock, $2->debugEntries);
						ip = psCondBlock->pCode;

						/* Put in the debugging information */
						PUT_DEBUG(psCondBlock, $2);

						/* Store the statements */
						PUT_BLOCK(ip, $2);
						FREE_DEBUG($2);
						FREE_BLOCK($2);

						$$ = psCondBlock;
					}
				;

cond_clause:		IF '(' boolexp ')'
					{
						char *pDummy;

						RULE( "cond_clause: IF '(' boolexp ')' '{' statement_list '}'");

						/* Get the line number for the end of the boolean expression */
						/* and store it in debugLine.                                 */
						scriptGetErrorData((SDWORD *)&debugLine, &pDummy);
					}
						'{' statement_list '}'
					{
						/* Allocate the block */
						ALLOC_CONDBLOCK(psCondBlock, 1,	//1 offset
										$3->size + $7->size +
										1 + 1);		//size + size + OP_JUMPFALSE + OP_JUMP

						ALLOC_DEBUG(psCondBlock, $7->debugEntries + 1);
						ip = psCondBlock->pCode;

						/* Store the boolean expression code */
						PUT_BLOCK(ip, $3);
						FREE_BLOCK($3);

						/* Put in the jump to the end of the block if the */
						/* condition is false */
						PUT_PKOPCODE(ip, OP_JUMPFALSE, $7->size + 2);

						/* Put in the debugging information */
						if (genDebugInfo)
						{
							psCondBlock->debugEntries = 1;
							psCondBlock->psDebug->line = debugLine;
							psCondBlock->psDebug->offset = 0;
							APPEND_DEBUG(psCondBlock, ip - psCondBlock->pCode, $7);
						}

						/* Store the statements */
						PUT_BLOCK(ip, $7);
						FREE_DEBUG($7);
						FREE_BLOCK($7);

						/* Store the location that has to be filled in   */
						//TODO: don't want to store pointers as ints
						psCondBlock->aOffsets[0] = (UDWORD)(ip - psCondBlock->pCode);

						/* Put in a jump to skip the rest of the conditional */
						/* The correct offset will be set once the whole   */
						/* conditional has been parsed                     */
						/* The jump should be to the instruction after the */
						/* entire conditonal block                         */
						PUT_PKOPCODE(ip, OP_JUMP, 0);

						$$ = psCondBlock;
					}
				/* 'Monoblock' */
				|		IF '(' boolexp ')'
					{
						char *pDummy;

						RULE( "cond_clause: IF '(' boolexp ')' statement");

						/* Get the line number for the end of the boolean expression */
						/* and store it in debugLine.                                 */
						scriptGetErrorData((SDWORD *)&debugLine, &pDummy);
					}
							statement
					{
						/* Allocate the block */
						ALLOC_CONDBLOCK(psCondBlock, 1,
										$3->size + $6->size +
										1 + 1);		//size + size + opcode + opcode

						ALLOC_DEBUG(psCondBlock, $6->debugEntries + 1);
						ip = psCondBlock->pCode;

						/* Store the boolean expression code */
						PUT_BLOCK(ip, $3);
						FREE_BLOCK($3);


						/* Put in the jump to the end of the block if the */
						/* condition is false */
						PUT_PKOPCODE(ip, OP_JUMPFALSE, $6->size + 2);

						/* Put in the debugging information */
						if (genDebugInfo)
						{
							psCondBlock->debugEntries = 1;
							psCondBlock->psDebug->line = debugLine;
							psCondBlock->psDebug->offset = 0;
							APPEND_DEBUG(psCondBlock, ip - psCondBlock->pCode, $6);
						}

						/* Store the statements */
						PUT_BLOCK(ip, $6);
						FREE_DEBUG($6);
						FREE_BLOCK($6);

						/* Store the location that has to be filled in   */
						psCondBlock->aOffsets[0] = (UDWORD)(ip - psCondBlock->pCode);

						/* Put in a jump to skip the rest of the conditional */
						/* The correct offset will be set once the whole   */
						/* conditional has been parsed                     */
						/* The jump should be to the instruction after the */
						/* entire conditonal block                         */
						PUT_PKOPCODE(ip, OP_JUMP, 0);

						$$ = psCondBlock;
					}
				;


	/*
	 * while loops, e.g.
	 *
	 * while (i < 5)
	 * {
	 *    ....
	 *    i = i + 1;
	 * }
	 */
loop:			WHILE '(' boolexp ')'
				{
					char *pDummy;

					/* Get the line number for the end of the boolean expression */
					/* and store it in debugLine.                                 */
					scriptGetErrorData((SDWORD *)&debugLine, &pDummy);
				}
								 '{' statement_list '}'
				{
					RULE("loop: WHILE '(' boolexp ')'");

					//ALLOC_BLOCK(psCurrBlock, $3->size + $7->size + sizeof(OPCODE) * 2);
					ALLOC_BLOCK(psCurrBlock, $3->size + $7->size + 1 + 1);	//size + size + opcode + opcode

					ALLOC_DEBUG(psCurrBlock, $7->debugEntries + 1);
					ip = psCurrBlock->pCode;

					/* Copy in the loop expression */
					PUT_BLOCK(ip, $3);
					FREE_BLOCK($3);

					/* Now a conditional jump out of the loop if the */
					/* expression is false.                          */
					PUT_PKOPCODE(ip, OP_JUMPFALSE, $7->size + 2);

					/* Now put in the debugging information */
					if (genDebugInfo)
					{
						psCurrBlock->debugEntries = 1;
						psCurrBlock->psDebug->line = debugLine;
						psCurrBlock->psDebug->offset = 0;
						APPEND_DEBUG(psCurrBlock, ip - psCurrBlock->pCode, $7);
					}

					/* Copy in the body of the loop */
					PUT_BLOCK(ip, $7);
					FREE_DEBUG($7);
					FREE_BLOCK($7);

					/* Put in a jump back to the start of the loop expression */
					PUT_PKOPCODE(ip, OP_JUMP, (SWORD)( -(SWORD)(psCurrBlock->size) + 1));

					$$ = psCurrBlock;
				}
		;

/* Increment/Decrement expressions */
inc_dec_exp:	NUM_VAR _INC
				{
					RULE("expression: NUM_VAR++");

					scriptCodeIncDec($1, &psCurrBlock, OP_INC);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	NUM_VAR _DEC
				{
					RULE("expression: NUM_VAR--");

					scriptCodeIncDec($1, &psCurrBlock, OP_DEC);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;

	/**************************************************************************************
	 *
	 * Mathematical expressions
	 */

expression:		expression '+' expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_ADD, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression '-' expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_SUB, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression '*' expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_MUL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression '/' expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_DIV, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}

			|	'-' expression %prec UMINUS
				{
					//ALLOC_BLOCK(psCurrBlock, $2->size + sizeof(OPCODE));
					ALLOC_BLOCK(psCurrBlock, $2->size + 1);	//size + unary minus opcode

					ip = psCurrBlock->pCode;

					/* Copy the already generated bits of code into the code block */
					PUT_BLOCK(ip, $2);

					/* Now put a negation operator into the code */
					PUT_PKOPCODE(ip, OP_UNARYOP, OP_NEG);

					/* Free the two code blocks that have been copied */
					FREE_BLOCK($2);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	'(' expression ')'
				{
					/* Just pass the code up the tree */
					$$ = $2;
				}
			|	TO_INT_CAST floatexp
				{
					RULE("expression: (int) floatexp");

					/* perform cast */
					$2->type = VAL_INT;

					//ALLOC_BLOCK(psCurrBlock, $4->size + sizeof(OPCODE));
					ALLOC_BLOCK(psCurrBlock, $2->size + 1);		//size + opcode
					ip = psCurrBlock->pCode;

					PUT_BLOCK(ip, $2);
					PUT_OPCODE(ip, OP_TO_INT);

					FREE_BLOCK($2);		// free floatexp

					$$ = psCurrBlock;
				}
			|	NUM_FUNC '(' param_list ')'
				{

					RULE( "expression: NUM_FUNC '(' param_list ')'");

					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}

			|	NUM_FUNC_CUST '(' param_list ')'
				{
					UDWORD paramNumber;

					RULE( "expression: NUM_FUNC_CUST '(' param_list ')'");

					/* if($4->numParams != $3->numParams) */
					if($3->numParams != $1->numParams)
					{
						debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
						scr_error("Wrong number of arguments in function call");
						return CE_PARSE;
					}

					if(!$1->bFunction)
					{
						debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
						scr_error("Can't call an event");
						return CE_PARSE;
					}

					/* make sure function has a return type */
					if($1->retType != VAL_INT)
					{
						debug(LOG_ERROR, "'%s' does not return an integer value", $1->pIdent);
						scr_error("assignment type conflict");
						return CE_PARSE;
					}

					/* check if right parameters were passed */
					paramNumber = checkFuncParamTypes($1, $3);
					if(paramNumber > 0)
					{
						debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
						YYABORT;
					}

					/* Allocate the code block */
					ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

					ip = psCurrBlock->pCode;

					if($3->numParams > 0)	/* if any parameters declared */
					{
						/* Copy in the code for the parameters */
						PUT_BLOCK(ip, $3);
					}

					FREE_PBLOCK($3);

					/* Store the instruction */
					PUT_OPCODE(ip, OP_FUNC);
					PUT_EVENT(ip,$1->index);			//Put event index

					$$ = psCurrBlock;
				}
			|	NUM_VAR
				{
					RULE("expression: NUM_VAR");

					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	NUM_CONSTANT
				{
					RULE("expression: NUM_CONSTANT");

					codeRet = scriptCodeConstant($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	num_objvar
				{
					RULE( "expression: num_objvar");
					RULE( "type=%d", $1->psObjVar->type);

					codeRet = scriptCodeObjGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	num_array_var
				{
					codeRet = scriptCodeArrayGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	INTEGER
				{
					RULE("expression: INTEGER");

					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + integer value

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_INT);
					PUT_DATA_INT(ip, $1);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;

floatexp:		floatexp '+' floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_ADD, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp '-' floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_SUB, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp '*' floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_MUL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp '/' floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_DIV, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	TO_FLOAT_CAST expression
				{
					RULE("floatexp: (float) expression");

					/* perform cast */
					 $2->type = VAL_FLOAT;

					ALLOC_BLOCK(psCurrBlock, $2->size + 1);	//size + opcode
					ip = psCurrBlock->pCode;

					PUT_BLOCK(ip, $2);
					PUT_OPCODE(ip, OP_TO_FLOAT);
					
					FREE_BLOCK($2);		// free 'expression'

					$$ = psCurrBlock;
				}
			|	'-' floatexp %prec UMINUS
				{
					ALLOC_BLOCK(psCurrBlock, $2->size + 1);	//size + opcode

					ip = psCurrBlock->pCode;

					/* Copy the already generated bits of code into the code block */
					PUT_BLOCK(ip, $2);

					/* Now put a negation operator into the code */
					PUT_PKOPCODE(ip, OP_UNARYOP, OP_NEG);

					/* Free the code block that have been copied */
					FREE_BLOCK($2);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	'(' floatexp ')'
				{
					/* Just pass the code up the tree */
					$$ = $2;
				}
			|	FLOAT_FUNC '(' param_list ')'
				{
					RULE("floatexp: FLOAT_FUNC '(' param_list ')'");

					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	FLOAT_FUNC_CUST '(' param_list ')'
				{
						UDWORD line,paramNumber;
						char *pDummy;

						RULE("floatexp: FLOAT_FUNC_CUST '(' param_list ')'");

						if($3->numParams != $1->numParams)
						{
							debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							scr_error("Wrong number of arguments in function call");
							return CE_PARSE;
						}

						if(!$1->bFunction)
						{
							debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
							scr_error("Can't call an event");
							return CE_PARSE;
						}

						/* make sure function has a return type */
						if($1->retType != VAL_FLOAT)
						{
							debug(LOG_ERROR, "'%s' does not return a float value", $1->pIdent);
							scr_error("assignment type conflict");
							return CE_PARSE;
						}

						/* check if right parameters were passed */
						paramNumber = checkFuncParamTypes($1, $3);
						if(paramNumber > 0)
						{
							debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);		//Put event/function index

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						$$ = psCurrBlock;
				}
			|	FLOAT_VAR
				{
					RULE( "floatexp: FLOAT_VAR");

					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	FLOAT_T
				{
					RULE( "floatexp: FLOAT_T");

					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(float));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);		//opcode + float

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_FLOAT);
					PUT_DATA_FLOAT(ip, $1);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;


	/***************************************************************************************
	 *
	 * String expressions
	 */
stringexp:
			stringexp '&' stringexp
				{
					RULE("stringexp: stringexp '&' stringexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;

					//debug(LOG_SCRIPT, "END stringexp: stringexp '&' stringexp");
				}
		| 	stringexp '&' expression
				{
					RULE( "stringexp: stringexp '&' expression");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		| 	expression '&' stringexp
				{
					RULE( "stringexp: expression '&' stringexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		| 	stringexp '&' boolexp
				{
					RULE( "stringexp: stringexp '&' boolexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		| 	boolexp '&' stringexp
				{
					RULE( "stringexp: boolexp '&' stringexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		| 	stringexp '&' floatexp
				{
					RULE( "stringexp: stringexp '&' floatexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		| 	floatexp '&' stringexp
				{
					RULE( "stringexp: floatexp '&' stringexp");

					codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	'(' stringexp ')'
				{
					/* Just pass the code up the tree */
					$$ = $2;
				}

			|	STRING_FUNC '(' param_list ')'
				{
					RULE("stringexp: STRING_FUNC '(' param_list ')'");

					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	STRING_FUNC_CUST '(' param_list ')'
				{
						RULE("stringexp: STRING_FUNC_CUST '(' param_list ')'");

						if($3->numParams != $1->numParams)
						{
							debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							scr_error("Wrong number of arguments in function call");
							return CE_PARSE;
						}

						if(!$1->bFunction)
						{
							debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
							scr_error("Can't call an event");
							return CE_PARSE;
						}

						/* make sure function has a return type */
						if($1->retType != VAL_STRING)
						{
							debug(LOG_ERROR, "'%s' does not return a string value", $1->pIdent);
							scr_error("assignment type conflict");
							return CE_PARSE;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);			//Put event index

						$$ = psCurrBlock;
				}

		|	STRING_VAR
				{
					RULE("stringexpr: STRING_VAR");

					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
		|	QTEXT
				{
					RULE("QTEXT: '%s'", yyvsp[0].sval);

					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + string pointer

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_STRING);
					PUT_DATA_STRING(ip, STRSTACK[CURSTACKSTR]);

					/* Return the code block */
					$$ = psCurrBlock;

					/* Manage string stack */
					sstrcpy(STRSTACK[CURSTACKSTR], yyvsp[0].sval);

					CURSTACKSTR = CURSTACKSTR + 1;		/* Increment 'pointer' to the top of the string stack */

					if(CURSTACKSTR >= MAXSTACKLEN)
					{
						scr_error("Can't store more than %d strings", MAXSTACKLEN);
					}

					//debug(LOG_SCRIPT, "END QTEXT found");
				}
		|		expression
				{
					RULE( "stringexp: expression");

					/* Just pass the code up the tree */
					$$ = $1;
				}
			;


	/***************************************************************************************
	 *
	 * Boolean expressions
	 */

boolexp:		boolexp _AND boolexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_AND, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	boolexp _OR boolexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_OR, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	boolexp BOOLEQUAL boolexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	boolexp NOTEQUAL boolexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	_NOT boolexp
				{
					//ALLOC_BLOCK(psCurrBlock, $2->size + sizeof(OPCODE));
					ALLOC_BLOCK(psCurrBlock, $2->size + 1);	//size + opcode

					ip = psCurrBlock->pCode;

					/* Copy the already generated bits of code into the code block */
					PUT_BLOCK(ip, $2);

					/* Now put a negation operator into the code */
					PUT_PKOPCODE(ip, OP_UNARYOP, OP_NOT);

					/* Free the two code blocks that have been copied */
					FREE_BLOCK($2);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	'(' boolexp ')'
				{
					/* Just pass the code up the tree */
					$$ = $2;
				}
			|	BOOL_FUNC '(' param_list ')'
				{
					RULE("boolexp: BOOL_FUNC '(' param_list ')'");

					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	BOOL_FUNC_CUST '(' param_list ')'
				{
						UDWORD paramNumber;

						RULE("boolexp: BOOL_FUNC_CUST '(' param_list ')'");

						if($3->numParams != $1->numParams)
						{
							debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							scr_error("Wrong number of arguments in function call");
							return CE_PARSE;
						}

						if(!$1->bFunction)
						{
							debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
							scr_error("Can't call an event");
							return CE_PARSE;
						}

						/* make sure function has a return type */
						if($1->retType != VAL_BOOL)
						{
							debug(LOG_ERROR, "'%s' does not return a boolean value", $1->pIdent);
							scr_error("assignment type conflict");
							return CE_PARSE;
						}

						/* check if right parameters were passed */
						paramNumber = checkFuncParamTypes($1, $3);
						if(paramNumber > 0)
						{
							debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);		//Put event/function index

						$$ = psCurrBlock;
				}
			|	BOOL_VAR
				{
					RULE("boolexp: BOOL_VAR");

					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	BOOL_CONSTANT
				{
					RULE("boolexp: BOOL_CONSTANT");

					codeRet = scriptCodeConstant($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	bool_objvar
				{
					codeRet = scriptCodeObjGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	bool_array_var
				{
					codeRet = scriptCodeArrayGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	BOOLEAN_T
				{
					RULE("boolexp: BOOLEAN_T");

					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + value
					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_BOOL);
					PUT_DATA_BOOL(ip, $1);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression BOOLEQUAL expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp BOOLEQUAL floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	stringexp BOOLEQUAL stringexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	userexp BOOLEQUAL userexp
				{
					if (!interpCheckEquiv($1->type,$3->type))
					{
						scr_error("Type mismatch for equality");
						YYABORT;
					}
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	objexp BOOLEQUAL objexp
				{
					if (!interpCheckEquiv($1->type,$3->type))
					{
						scr_error("Type mismatch for equality");
						YYABORT;
					}
					codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression NOTEQUAL expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp NOTEQUAL floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	stringexp NOTEQUAL stringexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	userexp NOTEQUAL userexp
				{
					if (!interpCheckEquiv($1->type,$3->type))
					{
						scr_error("Type mismatch for inequality");
						YYABORT;
					}
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	objexp NOTEQUAL objexp
				{
					if (!interpCheckEquiv($1->type,$3->type))
					{
						scr_error("Type mismatch for inequality");
						YYABORT;
					}
					codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression LESSEQUAL expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_LESSEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp LESSEQUAL floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_LESSEQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression GREATEQUAL expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATEREQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp GREATEQUAL floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATEREQUAL, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression GREATER expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATER, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp GREATER floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATER, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	expression LESS expression
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_LESS, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	floatexp LESS floatexp
				{
					codeRet = scriptCodeBinaryOperator($1, $3, OP_LESS, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;

	/*************************************************************************************
	 *
	 * User variable expressions
	 */

userexp:		VAR
				{
					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	USER_CONSTANT
				{
					RULE("userexp: USER_CONSTANT");

					codeRet = scriptCodeConstant($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	user_objvar
				{
					codeRet = scriptCodeObjGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);
					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	user_array_var
				{
					RULE("userexp: user_array_var");
					
					codeRet = scriptCodeArrayGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	USER_FUNC '(' param_list ')'
				{
					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	USER_FUNC_CUST '(' param_list ')'
				{
						UDWORD line,paramNumber;
						char *pDummy;

						if($3->numParams != $1->numParams)
						{
							debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							scr_error("Wrong number of arguments in function call");
							return CE_PARSE;
						}

						if(!$1->bFunction)
						{
							debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
							scr_error("Can't call an event");
							return CE_PARSE;
						}

						/* check if right parameters were passed */
						paramNumber = checkFuncParamTypes($1, $3);
						if(paramNumber > 0)
						{
							debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

						ALLOC_DEBUG(psCurrBlock, 1);
						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
							FREE_PBLOCK($3);
						}

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);			//Put event index

						/* Add the debugging information */
						if (genDebugInfo)
						{
							psCurrBlock->psDebug[0].offset = 0;
							scriptGetErrorData((SDWORD *)&line, &pDummy);
							psCurrBlock->psDebug[0].line = line;
						}

						/* remember objexp type for further stuff, like myVar = objFunc(); to be able to check type equivalency */
						psCurrBlock->type = $1->retType;

						$$ = psCurrBlock;
				}
			|	TRIG_SYM
				{
					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + trigger index

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_TRIGGER);		//TODO: don't need to put VAL_TRIGGER here, since stored in ip->type now
					PUT_TRIGGER(ip, $1->index);

					psCurrBlock->type = VAL_TRIGGER;

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	INACTIVE
				{
					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + '-1' for 'inactive'

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_TRIGGER);
					PUT_TRIGGER(ip, -1);

					psCurrBlock->type = VAL_TRIGGER;

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	EVENT_SYM
				{
					//ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD));
					ALLOC_BLOCK(psCurrBlock, 1 + 1);	//opcode + event index

					ip = psCurrBlock->pCode;

					/* Code to store the value on the stack */
					PUT_PKOPCODE(ip, OP_PUSH, VAL_EVENT);
					PUT_EVENT(ip, $1->index);

					psCurrBlock->type = VAL_EVENT;

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;

	/*************************************************************************************
	 *
	 * Object expressions.
	 */

objexp:			OBJ_VAR
				{
					codeRet = scriptCodeVarGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	OBJ_CONSTANT
				{
					RULE("objexp: OBJ_CONSTANT");

					codeRet = scriptCodeConstant($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	OBJ_FUNC '(' param_list ')'
				{
					/* Generate the code for the function call */
					codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	OBJ_FUNC_CUST '(' param_list ')'
				{
						UDWORD paramNumber;

						if($3->numParams != $1->numParams)
						{
							debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of  %d.", $1->pIdent, $1->numParams, $3->numParams);
							scr_error("Wrong number of arguments in function call");
							return CE_PARSE;
						}

						if(!$1->bFunction)
						{
							debug(LOG_ERROR, "'%s' is not a function", $1->pIdent);
							scr_error("Can't call an event");
							return CE_PARSE;
						}

						/* make sure function has a return type */
						/* if($1->retType != OBJ_VAR) */
						if(asScrTypeTab[$1->retType - VAL_USERTYPESTART].accessType != AT_OBJECT)
						{
							scr_error("'%s' does not return an object value (%d )", $1->pIdent, $1->retType);
							return CE_PARSE;
						}

						/* check if right parameters were passed */
						paramNumber = checkFuncParamTypes($1, $3);
						if(paramNumber > 0)
						{
							debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter  %d.", $1->pIdent, paramNumber);
							YYABORT;
						}

						/* Allocate the code block */
						ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1);	//Params + Opcode + event index

						ip = psCurrBlock->pCode;

						if($3->numParams > 0)	/* if any parameters declared */
						{
							/* Copy in the code for the parameters */
							PUT_BLOCK(ip, $3);
						}

						FREE_PBLOCK($3);

						/* Store the instruction */
						PUT_OPCODE(ip, OP_FUNC);
						PUT_EVENT(ip,$1->index);			//Put event index

						/* remember objexp type for further stuff, like myVar = objFunc(); to be able to check type equivalency */
						psCurrBlock->type = $1->retType;

						$$ = psCurrBlock;
				}
			|	obj_objvar
				{
					codeRet = scriptCodeObjGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			|	obj_array_var
				{
					codeRet = scriptCodeArrayGet($1, &psCurrBlock);
					CHECK_CODE_ERROR(codeRet);

					/* Return the code block */
					$$ = psCurrBlock;
				}
			;

	/*
	 * Seperate rule for the dot after an object so the lookup context
	 * for object variables can be set
	 */
objexp_dot:		objexp '.'
				{
					RULE( "objexp_dot: objexp '.', type=%d", $1->type);

					// Store the object type for the variable lookup
					objVarContext = $1->type;
				}
			|
				userexp '.'
				{
					RULE( "objexp_dot: userexp '.', type=%d", $1->type);

					// Store the object type for the variable lookup
					objVarContext = $1->type;
				}
			;

	/*
	 * Object member variable references.
	 */

num_objvar:		objexp_dot NUM_OBJVAR
				{

					RULE( "num_objvar: objexp_dot NUM_OBJVAR");

					codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock);
					CHECK_CODE_ERROR(codeRet);

					// reset the object type
					objVarContext = (INTERP_TYPE)0;

					/* Return the code block */
					$$ = psObjVarBlock;
				}
			;

bool_objvar:	objexp_dot BOOL_OBJVAR
				{
					codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock);
					CHECK_CODE_ERROR(codeRet);

					// reset the object type
					objVarContext = (INTERP_TYPE)0;

					/* Return the code block */
					$$ = psObjVarBlock;
				}
			;

user_objvar:	objexp_dot USER_OBJVAR
				{
					codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock);
					CHECK_CODE_ERROR(codeRet);

					// reset the object type
					objVarContext = (INTERP_TYPE)0;

					/* Return the code block */
					$$ = psObjVarBlock;
				}
			;
obj_objvar:		objexp_dot OBJ_OBJVAR
				{
					codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock);
					CHECK_CODE_ERROR(codeRet);

					// reset the object type
					objVarContext = (INTERP_TYPE)0;

					/* Return the code block */
					$$ = psObjVarBlock;
				}
			;

	/*****************************************************************************************************
	 *
	 * Array expressions
	 *
	 */

array_index:		'[' expression ']'
					{
						ALLOC_ARRAYBLOCK(psCurrArrayBlock, $2->size, NULL);
						ip = psCurrArrayBlock->pCode;

						/* Copy the index expression code into the code block */
						PUT_BLOCK(ip, $2);
						FREE_BLOCK($2);

						$$ = psCurrArrayBlock;
					}
			;

array_index_list:	array_index
					{
						$$ = $1;
					}
				|
					array_index_list '[' expression ']'
					{
						ALLOC_ARRAYBLOCK(psCurrArrayBlock, $1->size + $3->size, NULL);

						ip = psCurrArrayBlock->pCode;

						/* Copy the index expression code into the code block */
						psCurrArrayBlock->dimensions = $1->dimensions + 1;
						PUT_BLOCK(ip, $1);
						PUT_BLOCK(ip, $3);
						FREE_ARRAYBLOCK($1);
						FREE_ARRAYBLOCK($3);

						$$ = psCurrArrayBlock;
					}
				;

num_array_var:		NUM_ARRAY array_index_list
					{
						codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrArrayBlock;
					}
				;

bool_array_var:		BOOL_ARRAY array_index_list
					{
						codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrArrayBlock;
					}
				;

obj_array_var:		OBJ_ARRAY array_index_list
					{
						codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrArrayBlock;
					}
				;

user_array_var:		VAR_ARRAY array_index_list
					{
						RULE("user_array_var:		VAR_ARRAY array_index_list");
						
						codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock);
						CHECK_CODE_ERROR(codeRet);

						/* Return the code block */
						$$ = psCurrArrayBlock;
					}
				;

%%

// Reset all the symbol tables
static void scriptResetTables(void)
{
	VAR_SYMBOL		*psCurr, *psNext;
	TRIGGER_SYMBOL	*psTCurr, *psTNext;
	EVENT_SYMBOL	*psECurr, *psENext;

	SDWORD			i;

	/* start with global vars definition */
	localVariableDef = false;
	//debug(LOG_SCRIPT, "localVariableDef = false 4");

	/* Reset the global variable symbol table */
	for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psNext)
	{
		psNext = psCurr->psNext;
		free(psCurr);
	}
	psGlobalVars = NULL;

	/* Reset the global variable symbol table */
	psCurEvent = NULL;
	for(i=0; i<maxEventsLocalVars; i++)
	{
		numEventLocalVars[i] = 0;

		for(psCurr = psLocalVarsB[i]; psCurr != NULL; psCurr = psNext)
		{
			psNext = psCurr->psNext;
			free(psCurr);
		}
		psLocalVarsB[i] = NULL;
	}

	/* Reset the global array symbol table */
	for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psNext)
	{
		psNext = psCurr->psNext;
		free(psCurr);
	}
	psGlobalArrays = NULL;

	// Reset the trigger table
	for(psTCurr = psTriggers; psTCurr; psTCurr = psTNext)
	{
		psTNext = psTCurr->psNext;
		if (psTCurr->psDebug)
		{
			free(psTCurr->psDebug);
		}
		if (psTCurr->pCode)
		{
			free(psTCurr->pCode);
		}
		free(psTCurr->pIdent);
		free(psTCurr);
	}
	psTriggers = NULL;
	numTriggers = 0;

	// Reset the event table
	for(psECurr = psEvents; psECurr; psECurr = psENext)
	{
		psENext = psECurr->psNext;
		if (psECurr->psDebug)
		{
			free(psECurr->psDebug);
		}
		free(psECurr->pIdent);
		free(psECurr->pCode);
		free(psECurr);
	}
	psEvents = NULL;
	numEvents = 0;
}

/* Compile a script program */
SCRIPT_CODE* scriptCompile(PHYSFS_file* fileHandle, SCR_DEBUGTYPE debugType)
{
	/* Feed flex with initial input buffer */
	scriptSetInputFile(fileHandle);

	scriptResetTables();
	psFinalProg = NULL;
	if (debugType == SCR_DEBUGINFO)
	{
		genDebugInfo = true;
	}
	else
	{
		genDebugInfo = false;
	}

	if (scr_parse() != 0 || bError)
	{
		scr_lex_destroy();
		return NULL;
	}
	scr_lex_destroy();

	scriptResetTables();

	return psFinalProg;
}


/* A simple error reporting routine */
void scr_error(const char *pMessage, ...)
{
	int		line;
	char	*text;
	va_list	args;
	char	aBuff[1024];

	va_start(args, pMessage);
	vsnprintf(aBuff, sizeof(aBuff), pMessage, args);
	va_end(args);

	scriptGetErrorData(&line, &text);

	bError = true;

#ifdef DEBUG
	debug( LOG_ERROR, "script parse error:\n%s at %s:%d\nToken: %d, Text: '%s'\n",
			  aBuff, GetLastResourceFilename(), line, scr_char, text );
	ASSERT( false, "script parse error:\n%s at %s:%d\nToken: %d, Text: '%s'\n",
			  aBuff, GetLastResourceFilename(), line, scr_char, text );
#else
	//DBERROR(("script parse error:\n%s at line %d\nToken: %d, Text: '%s'\n",
	//		  pMessage, line, scr_char, text));
	debug( LOG_ERROR, "script parse error:\n'%s' at %s:%d\nToken: %d, Text: '%s'\n",
			  aBuff, GetLastResourceFilename(), line, scr_char, text );
	abort();
#endif
}


/* Look up a type symbol */
bool scriptLookUpType(const char *pIdent, INTERP_TYPE *pType)
{
	UDWORD	i;

	//debug(LOG_SCRIPT, "scriptLookUpType");

	if (asScrTypeTab)
	{
		for(i=0; asScrTypeTab[i].typeID != 0; i++)
		{
			if (strcmp(asScrTypeTab[i].pIdent, pIdent) == 0)
			{
				*pType = asScrTypeTab[i].typeID;
				return true;
			}
		}
	}

	//debug(LOG_SCRIPT, "END scriptLookUpType");

	return false;
}

/* pop passed arguments (if any) */
bool popArguments(INTERP_VAL **ip_temp, SDWORD numParams)
{
	SDWORD			i;

	/* code to pop passed params right before the main code begins */
	for(i = numParams-1; i >= 0 ; i--)
	{
		PUT_PKOPCODE(*ip_temp, OP_POPLOCAL, i);		//pop parameters into first i local params
	}

	return true;
}


/* Add a new variable symbol.
 * If localVariableDef is true a local variable symbol is defined,
 * otherwise a global variable symbol is defined.
 */
bool scriptAddVariable(VAR_DECL *psStorage, VAR_IDENT_DECL *psVarIdent)
{
	VAR_SYMBOL		*psNew;
	unsigned int i;
	char* ident;

	/* Allocate the memory for the symbol structure plus the symbol name */
	psNew = (VAR_SYMBOL *)malloc(sizeof(VAR_SYMBOL) + strlen(psVarIdent->pIdent) + 1);
	if (psNew == NULL)
	{
		scr_error("Out of memory");
		return false;
	}

	// This ensures we only need to call free() on the VAR_SYMBOL* pointer and not on its members
	ident = (char*)(psNew + 1);
	strcpy(ident, psVarIdent->pIdent);
	psNew->pIdent = ident;

	psNew->type = psStorage->type;
	psNew->storage = psStorage->storage;
	psNew->dimensions = psVarIdent->dimensions;


	for(i=0; i<psNew->dimensions; i++)
	{
		psNew->elements[i] = psVarIdent->elements[i];
	}
	if (psNew->dimensions == 0)
	{
		if(psStorage->storage != ST_LOCAL)	//if not a local var
		{
			if (psGlobalVars == NULL)
			{
				psNew->index = 0;
			}
			else
			{
				psNew->index = psGlobalVars->index + 1;
			}

			/* Add the symbol to the list */
			psNew->psNext = psGlobalVars;
			psGlobalVars = psNew;
		}
		else		//local var
		{
			if(psCurEvent == NULL)
				debug(LOG_ERROR, "Can't declare local variables before defining an event");

			//debug(LOG_SCRIPT, "local variable declared for event %d, type=%d \n", psCurEvent->index, psNew->type);
			//debug(LOG_SCRIPT, "%s \n", psNew->pIdent);

			if (psLocalVarsB[psCurEvent->index] == NULL)
			{
				psNew->index = 0;
			}
			else
			{
				psNew->index = psLocalVarsB[psCurEvent->index]->index + 1;
			}

			numEventLocalVars[psCurEvent->index] = numEventLocalVars[psCurEvent->index] + 1;

			psNew->psNext = psLocalVarsB[psCurEvent->index];
			psLocalVarsB[psCurEvent->index] = psNew;

			//debug(LOG_SCRIPT, "local variable declared. ");
		}
	}
	else
	{
		if (psGlobalArrays == NULL)
		{
			psNew->index = 0;
		}
		else
		{
			psNew->index = psGlobalArrays->index + 1;
		}

		psNew->psNext = psGlobalArrays;
		psGlobalArrays = psNew;
	}


	return true;
}

/* Look up a variable symbol */
bool scriptLookUpVariable(const char *pIdent, VAR_SYMBOL **ppsSym)
{
	VAR_SYMBOL		*psCurr;
	UDWORD			i;

	//debug(LOG_SCRIPT, "scriptLookUpVariable");

	/* See if the symbol is an object variable */
	if (asScrObjectVarTab && objVarContext != 0)
	{
		for(psCurr = asScrObjectVarTab; psCurr->pIdent != NULL; psCurr++)
		{
			if (interpCheckEquiv((INTERP_TYPE)psCurr->objType, objVarContext) &&
				strcmp(psCurr->pIdent, pIdent) == 0)
			{
				*ppsSym = psCurr;
				return true;
			}
		}
	}

	/* See if the symbol is an external variable */
	if (asScrExternalTab)
	{
		for(psCurr = asScrExternalTab; psCurr->pIdent != NULL; psCurr++)
		{
			if (strcmp(psCurr->pIdent, pIdent) == 0)
			{
				//debug(LOG_SCRIPT, "scriptLookUpVariable: extern");
				*ppsSym = psCurr;
				return true;
			}
		}
	}

	/* check local vars if we are inside of an event */
	if(psCurEvent != NULL)
	{
		if(psCurEvent->index >= maxEventsLocalVars)
			debug(LOG_ERROR, "psCurEvent->index (%d) >= maxEventsLocalVars", psCurEvent->index);

		i = psCurEvent->index;

		if(psLocalVarsB[i] != NULL)	//any vars stored for this event
		{
		int		line;
		char	*text;

			//debug(LOG_SCRIPT, "now checking event %s; index = %d\n", psCurEvent->pIdent, psCurEvent->index);
			scriptGetErrorData(&line, &text);
			for(psCurr = psLocalVarsB[i]; psCurr != NULL; psCurr = psCurr->psNext)
			{

				if(psCurr->pIdent == NULL)
				{
					debug(LOG_ERROR, "psCurr->pIdent == NULL");
					debug(LOG_ERROR, "psCurr->index = %d", psCurr->index);
				}

				//debug(LOG_SCRIPT, "start comparing, num local vars=%d, at line %d\n", numEventLocalVars[i], line);
				//debug(LOG_SCRIPT, "current var=%s\n", psCurr->pIdent);
				//debug(LOG_SCRIPT, "passed string=%s\n", pIdent);

				//debug(LOG_SCRIPT, "comparing %s with %s \n", psCurr->pIdent, pIdent);
				if (strcmp(psCurr->pIdent, pIdent) == 0)
				{
					//debug(LOG_SCRIPT, "scriptLookUpVariable - local var found, type=%d\n", psCurr->type);
					*ppsSym = psCurr;
					return true;
				}
			}
		}
	}

	/* See if the symbol is in the global variable list.
	 * This is not checked for when local variables are being defined.
	 * This allows local variables to have the same name as global ones.
	 */
	if (!localVariableDef)
	{
		for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psCurr->psNext)
		{
			if (strcmp(psCurr->pIdent, pIdent) == 0)
			{
				*ppsSym = psCurr;
				return true;
			}
		}
		for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psCurr->psNext)
		{
			if (strcmp(psCurr->pIdent, pIdent) == 0)
			{
				*ppsSym = psCurr;
				return true;
			}
		}
	}

	/* Failed to find the variable */
	*ppsSym = NULL;

	//debug(LOG_SCRIPT, "END scriptLookUpVariable");
	return false;
}


/* Add a new trigger symbol */
bool scriptAddTrigger(const char *pIdent, TRIGGER_DECL *psDecl, UDWORD line)
{
	TRIGGER_SYMBOL		*psTrigger, *psCurr, *psPrev;

	// Allocate the trigger
	psTrigger = (TRIGGER_SYMBOL *)malloc(sizeof(TRIGGER_SYMBOL));
	if (!psTrigger)
	{
		scr_error("Out of memory");
		return false;
	}
	psTrigger->pIdent = (char *)malloc(strlen(pIdent) + 1);
	if (!psTrigger->pIdent)
	{
		scr_error("Out of memory");
		return false;
	}
	strcpy(psTrigger->pIdent, pIdent);
	if (psDecl->size > 0)
	{
		psTrigger->pCode = (INTERP_VAL *)malloc(psDecl->size * sizeof(INTERP_VAL));
		if (!psTrigger->pCode)
		{
			scr_error("Out of memory");
			return false;
		}
		memcpy(psTrigger->pCode, psDecl->pCode, psDecl->size * sizeof(INTERP_VAL));
	}
	else
	{
		psTrigger->pCode = NULL;
	}
	psTrigger->size = psDecl->size;
	psTrigger->type = psDecl->type;
	psTrigger->time = psDecl->time;
	psTrigger->index = numTriggers++;
	psTrigger->psNext = NULL;

	// Add debug info
	if (genDebugInfo)
	{
		psTrigger->psDebug = (SCRIPT_DEBUG *)malloc(sizeof(SCRIPT_DEBUG));
		psTrigger->psDebug[0].offset = 0;
		psTrigger->psDebug[0].line = line;
		psTrigger->debugEntries = 1;
	}
	else
	{
		psTrigger->debugEntries = 0;
		psTrigger->psDebug = NULL;
	}


	// Store the trigger
	psPrev = NULL;
	for(psCurr = psTriggers; psCurr; psCurr = psCurr->psNext)
	{
		psPrev = psCurr;
	}
	if (psPrev)
	{
		psPrev->psNext = psTrigger;
	}
	else
	{
		psTriggers = psTrigger;
	}

	return true;
}


/* Lookup a trigger symbol */
bool scriptLookUpTrigger(const char *pIdent, TRIGGER_SYMBOL **ppsTrigger)
{
	TRIGGER_SYMBOL	*psCurr;

	//debug(LOG_SCRIPT, "scriptLookUpTrigger");

	for(psCurr = psTriggers; psCurr; psCurr=psCurr->psNext)
	{
		if (strcmp(pIdent, psCurr->pIdent) == 0)
		{
			//debug(LOG_SCRIPT, "scriptLookUpTrigger: found");
			*ppsTrigger = psCurr;
			return true;
		}
	}

	//debug(LOG_SCRIPT, "END scriptLookUpTrigger");

	return false;
}


/* Lookup a callback trigger symbol */
bool scriptLookUpCallback(const char *pIdent, CALLBACK_SYMBOL **ppsCallback)
{
	CALLBACK_SYMBOL		*psCurr;

	//debug(LOG_SCRIPT, "scriptLookUpCallback");

	if (!asScrCallbackTab)
	{
		return false;
	}

	for(psCurr = asScrCallbackTab; psCurr->type != 0; psCurr += 1)
	{
		if (strcmp(pIdent, psCurr->pIdent) == 0)
		{
			//debug(LOG_SCRIPT, "scriptLookUpCallback: found");
			*ppsCallback = psCurr;
			return true;
		}
	}

	//debug(LOG_SCRIPT, "END scriptLookUpCallback: found");
	return false;
}

/* Add a new event symbol */
bool scriptDeclareEvent(const char *pIdent, EVENT_SYMBOL **ppsEvent, SDWORD numArgs)
{
	EVENT_SYMBOL		*psEvent, *psCurr, *psPrev;

	// Allocate the event
	psEvent = (EVENT_SYMBOL *)malloc(sizeof(EVENT_SYMBOL));
	if (!psEvent)
	{
		scr_error("Out of memory");
		return false;
	}
	psEvent->pIdent = (char *)malloc(strlen(pIdent) + 1);
	if (!psEvent->pIdent)
	{
		scr_error("Out of memory");
		return false;
	}
	strcpy(psEvent->pIdent, pIdent);
	psEvent->pCode = NULL;
	psEvent->size = 0;
	psEvent->psDebug = NULL;
	psEvent->debugEntries = 0;
	psEvent->index = numEvents++;
	psEvent->psNext = NULL;

	/* remember how many params this event has */
	psEvent->numParams = numArgs;
	psEvent->bFunction = false;
	psEvent->bDeclared = false;
	psEvent->retType = VAL_VOID;	/* functions can return a value */

	// Add the event to the list
	psPrev = NULL;
	for(psCurr = psEvents; psCurr; psCurr = psCurr->psNext)
	{
		psPrev = psCurr;
	}
	if (psPrev)
	{
		psPrev->psNext = psEvent;
	}
	else
	{
		psEvents = psEvent;
	}

	*ppsEvent = psEvent;

	return true;
}

// Add the code to a defined event
bool scriptDefineEvent(EVENT_SYMBOL *psEvent, CODE_BLOCK *psCode, SDWORD trigger)
{
	ASSERT(psCode != NULL, "scriptDefineEvent: psCode == NULL");
	ASSERT(psCode->size > 0,
		"Event '%s' is empty, please add at least 1 statement", psEvent->pIdent);

	// events with arguments can't have a trigger assigned
	ASSERT(!(psEvent->numParams > 0 && trigger >= 0),
		"Events with parameters can't have a trigger assigned, event: '%s' ", psEvent->pIdent);

	// Store the event code
	psEvent->pCode = (INTERP_VAL *)malloc(psCode->size * sizeof(INTERP_VAL));
	if (!psEvent->pCode)
	{
		scr_error("Out of memory");
		return false;
	}

	memcpy(psEvent->pCode, psCode->pCode, psCode->size * sizeof(INTERP_VAL));
	psEvent->size = psCode->size;
	psEvent->trigger = trigger;

	// Add debug info
	if (genDebugInfo && (psCode->debugEntries > 0))
	{
		psEvent->psDebug = (SCRIPT_DEBUG *)malloc(sizeof(SCRIPT_DEBUG) * psCode->debugEntries);

		if (!psEvent->psDebug)
		{
			scr_error("Out of memory");
			return false;
		}

		memcpy(psEvent->psDebug, psCode->psDebug,
			sizeof(SCRIPT_DEBUG) * psCode->debugEntries);
		psEvent->debugEntries = psCode->debugEntries;
	}
	else
	{
		psEvent->debugEntries = 0;
		psEvent->psDebug = NULL;
	}

	//debug(LOG_SCRIPT, "before define event");

	/* store local vars */
	if(psEvent->index >= maxEventsLocalVars)
		debug(LOG_ERROR, "scriptDefineEvent - psEvent->index >= maxEventsLocalVars");

	return true;
}

/* Lookup an event symbol */
bool scriptLookUpEvent(const char *pIdent, EVENT_SYMBOL **ppsEvent)
{
	EVENT_SYMBOL	*psCurr;
	//debug(LOG_SCRIPT, "scriptLookUpEvent");

	for(psCurr = psEvents; psCurr; psCurr=psCurr->psNext)
	{
		if (strcmp(pIdent, psCurr->pIdent) == 0)
		{
			//debug(LOG_SCRIPT, "scriptLookUpEvent:found");
			*ppsEvent = psCurr;
			return true;
		}
	}
	//debug(LOG_SCRIPT, "END scriptLookUpEvent");
	return false;
}


/* Look up a constant variable symbol */
bool scriptLookUpConstant(const char *pIdent, CONST_SYMBOL **ppsSym)
{
	CONST_SYMBOL	*psCurr;

	//debug(LOG_SCRIPT, "scriptLookUpConstant");

	/* Scan the Constant list */
	if (asScrConstantTab)
	{
		for(psCurr = asScrConstantTab; psCurr->type != VAL_VOID; psCurr++)
		{
			if (strcmp(psCurr->pIdent, pIdent) == 0)
			{
				*ppsSym = psCurr;
				return true;
			}
		}
	}

	//debug(LOG_SCRIPT, "END scriptLookUpConstant");

	return false;
}


/* Look up a function symbol */
bool scriptLookUpFunction(const char *pIdent, FUNC_SYMBOL **ppsSym)
{
	UDWORD i;

	//debug(LOG_SCRIPT, "scriptLookUpFunction");

	/* See if the function is defined as an instinct function */
	if (asScrInstinctTab)
	{
		for(i = 0; asScrInstinctTab[i].pFunc != NULL; i++)
		{
			if (strcmp(asScrInstinctTab[i].pIdent, pIdent) == 0)
			{
				*ppsSym = asScrInstinctTab + i;
				return true;
			}
		}
	}

	/* Failed to find the indentifier */
	*ppsSym = NULL;

	//debug(LOG_SCRIPT, "END scriptLookUpFunction");
	return false;
}

/* Look up a function symbol defined in script */
bool scriptLookUpCustomFunction(const char *pIdent, EVENT_SYMBOL **ppsSym)
{
	EVENT_SYMBOL	*psCurr;

	//debug(LOG_SCRIPT, "scriptLookUpCustomFunction");

	/* See if the function is defined as a script function */
	for(psCurr = psEvents; psCurr; psCurr = psCurr->psNext)
	{
		if(psCurr->bFunction)	/* event defined as function */
		{
			if (strcmp(psCurr->pIdent, pIdent) == 0)
			{
				//debug(LOG_SCRIPT, "scriptLookUpCustomFunction: %s is a custom function", pIdent);
				*ppsSym = psCurr;
				return true;
			}
		}
	}

	/* Failed to find the indentifier */
	*ppsSym = NULL;

	//debug(LOG_SCRIPT, "END scriptLookUpCustomFunction");
	return false;
}


/* Set the type table */
void scriptSetTypeTab(TYPE_SYMBOL *psTypeTab)
{
#ifdef DEBUG
	SDWORD			i;
	INTERP_TYPE		type;

	for(i=0, type=VAL_USERTYPESTART; psTypeTab[i].typeID != 0; i++)
	{
		ASSERT( psTypeTab[i].typeID == type,
			"scriptSetTypeTab: ID's must be >= VAL_USERTYPESTART and sequential" );
		type = (INTERP_TYPE)(type + 1);
	}
#endif

	asScrTypeTab = psTypeTab;
}


/* Set the function table */
void scriptSetFuncTab(FUNC_SYMBOL *psFuncTab)
{
	asScrInstinctTab = psFuncTab;
}

/* Set the object variable table */
void scriptSetObjectTab(VAR_SYMBOL *psObjTab)
{
	asScrObjectVarTab = psObjTab;
}

/* Set the external variable table */
void scriptSetExternalTab(VAR_SYMBOL *psExtTab)
{
	asScrExternalTab = psExtTab;
}

/* Set the constant table */
void scriptSetConstTab(CONST_SYMBOL *psConstTab)
{
	asScrConstantTab = psConstTab;
}

/* Set the callback table */
void scriptSetCallbackTab(CALLBACK_SYMBOL *psCallTab)
{
#ifdef DEBUG
	SDWORD			i;
	TRIGGER_TYPE	type;

	for(i=0, type=TR_CALLBACKSTART; psCallTab[i].type != 0; i++)
	{
		ASSERT( psCallTab[i].type == type,
			"scriptSetCallbackTab: ID's must be >= VAL_CALLBACKSTART and sequential" );
		type = (TRIGGER_TYPE)(type + 1);
	}
#endif

	asScrCallbackTab = psCallTab;
}
