warzone2100/lib/script/script_parser.y

6336 lines
164 KiB
Plaintext

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2011 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
*/
%{
/*
* 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;
}